2

I have the following function template

template<typename T>
T foo(){
    if(std::is_same<T,int>::value){
        return 1;
    }
    if(std::is_same<T,std::string>::value){
        return "Hello";
    }
}

I want to apply it like this:

int main(){
    std::cout << foo<int>() << std::endl;    // 1
    std::cout << foo<string>() << std::endl;    // "Hello"
}

If I try to compile my template the compiler throws the following error: error: cannot initialize return object of type 'int' with an lvalue of type 'const char [6]'.

If I remove the second if statement everything compiles fine and I get the correct output therefore I guess the comparison std::is_same<T,int>::value works as intended.

It seems like the Compiler detects the type of T, checks if all return statements match it and throws the error because an std::string is not implicitly castable to int.

Has anyone a solution or another workaround to satisfy my intention?

EDIT

To explain my intention: I am writing a wrapper class for a database cursor class. As these cursors are usually defined it has several member functions like getInt() or getString(). My idea was to implement a generic get<T>() that uses the corresponding cursor member function that depends on T.

dtell
  • 2,488
  • 1
  • 14
  • 29
  • In C++17 you will get `if constexpr` that allows some parts not to compile. Right now, all parts of the code must be valid for *all* possible type `T`. You will have to add separate overloads of the function. – Bo Persson Apr 04 '17 at 22:50

2 Answers2

2

Forget the if statements and fully specialize the template function:

template<>
int foo<int>(){
    return 1;
}

template<>
std::string foo<std::string>(){
    return "Hello";
}
  • And to help understand why your original code didn't work, replace all the occurrences of `T` in your original template with `int`, which is essentially the result of invoking `foo()`. Then it becomes clear that you have a function declared to return an `int` trying to return a `string`. – Alain Apr 04 '17 at 22:56
  • Note: It's not explicit instantiation, but rather template specialization. [See here](http://stackoverflow.com/a/4933205/1116364). – Daniel Jour Apr 04 '17 at 22:58
  • 1
    I should have said fully specialized... edited. Thank you – Dustin Nieffenegger Apr 04 '17 at 23:20
  • You don't need the angle brackets in `foo` and `foo` – David G Apr 04 '17 at 23:20
1

When you reference foo<int>, the compiler generates a function like the following from your template:

int foo<int>(){
    if(true){
        return 1;
    }
    if(false){
        return "Hello";
    }
}

Above code is ill-typed, because (as the compiler tells you) an int cannot be initialized with a C string (char const [N]). Sure, the corresponding return statement will never be reached, but that's something to be figured out by "dead code elimination", which is merely an optimization.

The solution "now" is shown in Dustin's answer, with C++17 we'll get if constexpr which is able to discard code paths the way you expected it:

template<typename T>
T foo(){
    if constexpr (std::is_same<T,int>::value){
        return 1;
    }
    if constexpr (std::is_same<T,std::string>::value){
        return "Hello";
    }
}
Community
  • 1
  • 1
Daniel Jour
  • 15,896
  • 2
  • 36
  • 63