2

I am using the following sample program in C++

#include<iostream>
#include<ctime>
#include<cstdlib>

using namespace std;

namespace mine{
    template<class T>
    inline void swap(T &a, T &b){
        char c= a; //This should not have compiled
        a=b;
        b=c;
    }

}
int main(){
    int a,b;
    cout<< "Enter two values: ";
    cin>>a>>b;
    mine::swap(a,b); //type variable T is instantiated as in
    cout << a <<' '<<b << endl;
}

I am expecting the compiler to throw an error in the swap function, because c is declared as a char, but assigned a variable of generic type variable T. Not only that, when invoking swap, T is instantiated as int. However, not only is g++ not giving any error, the program works perfectly. Why is this the case?

user2833557
  • 677
  • 5
  • 10
  • How do you compile? `-Wall`? – nvoigt Nov 16 '17 at 14:24
  • 6
    Because you can assign `int`s to `char`s. `char c = 42;` compiles just fine. – nwp Nov 16 '17 at 14:24
  • 1
    The type of "_generic type variable_" is known at compile time, hence it knows that type of `T` is of `int`, due to the manner of invocation. And even then - only warning may have been given, at sufficiently high warning level, due to narrowing conversion. I don't see why the compiler should've refused to compile it. – Algirdas Preidžius Nov 16 '17 at 14:24
  • 2
    The code is valid; an `int` can be converted to `char`. The program does **not** work perfectly; try setting `a` and `b` to values that won't fit in a `char` (for example, 3000 and 4000). – Pete Becker Nov 16 '17 at 14:25
  • g++ -o test -Wall test.cpp Still compiles without any warning. – user2833557 Nov 16 '17 at 14:25
  • It is true that the code does not work properly. Then it seems like that compiler design is not typesafe when using generic implementations. – user2833557 Nov 16 '17 at 14:27
  • [g++ doesn't produce a warning with `-Wall -Wextra -pedantic`](http://coliru.stacked-crooked.com/a/5cfab7b3d46f83b8), so it is not obvious how to make the compiler warn here. – nwp Nov 16 '17 at 14:27
  • 5
    @user2833557 It has nothing to do with "generic implementations"; templates are completely typesafe (which is part of what makes them so useful). It has to do with the fact that `int`s are implicitly convertible to `char`s. – 0x5453 Nov 16 '17 at 14:27
  • 3
    For GCC to complain about these things, you usually need the `-Wconversion` flag. – StoryTeller - Unslander Monica Nov 16 '17 at 14:28
  • 1
    The compiler generates one version of `swap` for each value of `T` used in the code. In the posted code, the template is used only with `int` for `T` and the function is `void swap(int &, int &)`. Try using an array or a `struct` for `T` and the compiler will object, for sure. – axiac Nov 16 '17 at 14:29
  • 1
    yup, -Wconversion flag worked. thanks. – user2833557 Nov 16 '17 at 14:29
  • 1
    the answers here are good: [Warnings or errors for C++ implicit conversion of primitives](https://stackoverflow.com/questions/4477139/warnings-or-errors-for-c-implicit-conversion-of-primitives) – underscore_d Nov 16 '17 at 15:10

2 Answers2

8

C++ gives you the ability to shoot yourself in the foot.

The fact remains that any integral type is convertible to a char with implementation defined behaviour.

The compiler is assuming you know what you are doing, that's all.

auto c = a; would be the best replacement these days. Prior to C++11 you could have written T C = a; (you still can of course.) Although since C++11 you ought to use std::move when swapping, see how std::swap is implemented on your platform. (Reference How does the standard library implement std::swap?)

gcc will warn you of this if you specify -Wconversion on the command line.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
0

First of all, converting int to char is legal, it is a narrowing conversion, and the compiler might warn you if you configure it to do so.

As for why the code compiles, this is due to the fact that the type is known at compile time, therefore the compiler knows that for all the instances of T that it initialised, T is convertible to char.

If you will change a to a type that is not convertible to char, the compiler will complain: e.g with MSVC The following code give error C2440: 'initializing' : cannot convert from 'std::string' to 'char':

#include "stdafx.h"
#include<iostream>
#include<ctime>
#include<cstdlib>

using namespace std;

namespace mine{
    template<class T>
    inline void swap(T &a, T &b){
        char c= a; //This should not have compiled
        a=b;
        b=c;
    }

}

int _tmain(int argc, _TCHAR* argv[])
{
    string a("test");
    string b("test2");
    mine::swap(a,b); //type variable T is instantiated as in
}
OriBS
  • 722
  • 5
  • 9