Reputation:
In a function like the following:
template <typename T>
T foo () {
std::string str("foo");
if (typeid(T) == typeid(int)) return (T)(1); // Cast to T is required in order to make sure the function returns T
else if (typeid(T) == typeid(std::string)) return (T)str;
// ..
}
It will get a compile error because the compiler will go ahead and test all possible values of T on every single line, and there is no conversion from std::string to int, even though I already did a type-check. How should I approach this problem?
Upvotes: 0
Views: 71
Reputation: 596256
In C++17 and later, you can use if constexpr
with std::is_same_v
:
template <typename T>
T foo ()
{
if constexpr (std::is_same_v<T, int>)
return 1;
else if constexpr (std::is_same_v<T, std::string>)
return "foo";
// ..
}
This allows the compiler to optimize the if
s at compile-time and eliminate any unused branches from evaluation. For instance, when T
is int
then foo<int>()
will simply be:
int foo ()
{
return 1;
}
And when T
is std::string
then foo<std::string>()
will simply be:
std::string foo ()
{
return "foo";
}
Prior to C++17, you have to use template specialization instead to accomplish something similar, eg:
template <typename T>
T foo ()
{
return T();
}
template<>
int foo<int> ()
{
return 1;
}
template<>
std::string foo<std::string> ()
{
return "foo";
}
Alternatively, use the specialization on a helper function/struct, especially if foo()
has other code that is invoked regardless of the type of T
, eg:
template<typename T>
T someValue()
{
return T();
}
template<>
int someValue<int>()
{
return 1;
}
template<>
std::string someValue<std::string>()
{
return "foo";
}
template <typename T>
T foo ()
{
...
return someValue<T>();
}
template<typename T>
struct someType
{
static const T value = T();
}
template<>
struct someType<int>
{
static const int value = 1;
}
template<>
struct someType<std::string>
{
static const std::string value = "foo";
}
template <typename T>
T foo ()
{
...
return someType<T>::value;
}
Upvotes: 3
Reputation: 2598
Use if constexpr
:
#include <type_traits>
template <typename T>
T foo () {
std::string str("foo");
if constexpr (std::is_same_v<T, int>) return (T)(1); // Cast to T is required in order to make sure the function returns T
else if constexpr (std::is_same_v<T, std::string>) return (T)str;
// ..
}
This compiles the inner block only if the constant expression in its argument evaluates to a value considered true
. The syntax needs to be correct in the inner block, though. The is_same_v
checks if the types are the same.
Upvotes: 0