user13830821
user13830821

Reputation:

Type-checking in template function returning given type

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

Answers (2)

Remy Lebeau
Remy Lebeau

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 ifs 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

Anonymous1847
Anonymous1847

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

Related Questions