Just started exploring template feature for one of my task, I need to add some actions based on the typename in the template. Can someone point out what is wrong with this kind of structure:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
T foo()
{
if(std::is_same<T, int>::value)
{
return 2;
}
if(std::is_same<T, std::string>::value)
{
return "apple";
}
}
int main()
{
std::cout<<"foo is: "<<foo<int>()<<std::endl;
return 0;
}
I am wondering:
- Why this error is happening
main.cpp:23:16: error: invalid conversion from ‘const char*’ to ‘int’
and how to get rid of it? - Is there a better way to do specific actions based on
typename
provided to the function?
UPDATE:
It turned out my program is using below C 17 compiler
Attempt:
I tried another approach to handle this case but failed miserably:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T, typename U>
T foo()
{
T t = U;
return t;
}
int main()
{
std::cout<<"foo is: "<<foo<int, 1>()<<std::endl;
return 0;
}
Anyone can point out what is going wrong here?
CodePudding user response:
The problem is that, even if your T
is int
, all the branches still have to compile. So the second return statement causes an error because the string literal cannot be converted to the int
return value.
Since C 17, you can use if constexpr
to tell the compiler that the condition is a compile time constant which allows it to only compile the needed branches:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
T foo()
{
if constexpr(std::is_same<T, int>::value)
{
return 2;
}
if constexpr(std::is_same<T, std::string>::value)
{
return "apple";
}
}
int main()
{
std::cout<<"foo is: "<<foo<int>()<<std::endl;
return 0;
}
If you need a solution in earlier standards, you'll have to use template specialization like this:
#include <iostream>
#include <type_traits>
using namespace std;
//general template
template <typename T>
T foo();
//specialization for int
template <>
int foo<int>()
{
return 2;
}
//specialization for string
template <>
string foo<string>()
{
return "apple";
}
int main()
{
std::cout<<"foo is: "<<foo<int>()<<std::endl;
return 0;
}
CodePudding user response:
Template instantiations must be valid on all branches, which yours aren't. What I mean by that is this is how the functions generated by your template look like:
int foo<int>()
{
if(true)
{
return 2;
}
if(false)
{
return "apple"; // error
}
}
std::string foo<std::string>()
{
if(false)
{
return 2; // error
}
if(true)
{
return "apple";
}
}
Since C 17 you can use constexpr if to solve this:
template <typename T>
T foo()
{
if constexpr (std::is_same<T, int>::value)
{
return 2;
}
else if constexpr (std::is_same<T, std::string>::value)
{
return "apple";
}
}
CodePudding user response:
Another alternative to C 17's constexpr-if if you are in earlier language versions is tag dispatch:
#include <iostream>
#include <string>
#include <type_traits>
namespace detail {
template <typename T> struct Tag {};
int f_impl(Tag<int>) { return 2; }
std::string f_impl(Tag<std::string>) { return "apple"; }
} // namespace detail
template <typename T> T foo() { return detail::f_impl(detail::Tag<T>{}); }
int main() {
std::cout << "foo is: " << foo<int>() << std::endl;
return 0;
}
For this simple example specializations will do, but tag dispatch can be useful for more complex "if-else-dispatch" logic.