C++ template - using "std::is_same_v" instead of specializing and avoid compilation error?

To answer your question about templates (although in this particular application, it is not the right solution, for many reasons):

The reason it doesn't work as you wrote it is that template instantiation happens at compile-time, and the only thing happening then is that the value of std::is_same is computed for the template argument. Thus, in the code for function<solo> the line

if(std::is_same<T, duo>::value) std::cout<< std::to_string(test.b);

would be like

if(false) std::cout<< std::to_string(test.b);

which doesn't compile as there is no member b in test.

To make it work, you need two templates and use SFINAE to select the correct one when instantiating the template (and as function templates cannot be partially specialized, you need to write it something like the following, which is really a silly way of writing two overloads. Or you can fully specialize the template, but then you wouldn't use if_same).

template<class T>
typename std::enable_if<!std::is_same<T, duo>::value, void>::type function(T test){
 std::cout<< std::to_string(test.a);
}

template<class T>
typename std::enable_if<std::is_same<T, duo>::value, void>::type function(T test){
 std::cout<< std::to_string(test.a);
 std::cout<< std::to_string(test.b);
}

Further, note that is_same looks at the static type of the variable, so if you have a solo& to a duo object, it would still choose the solo overload.

A somewhat less silly use of templates is to write a function template that can handle any type that has a member int b. This uses a helper metafunction (a struct, so we can use partial specialization):

template <class T, class = int>
struct has_member_b : std::false_type {};

template <class T> 
struct has_member_b<T, decltype(std::declval<T>().b)> : std::true_type {};   

template<class T>
typename std::enable_if<has_member_b<T>::value, void>::type function(T test){
    std::cout<< std::to_string(test.a);
    std::cout<< std::to_string(test.b);
}

template<class T>
typename std::enable_if<!has_member_b<T>::value, void>::type function(T test) {
    std::cout<< std::to_string(test.a);
}

(Do note that both versions assume there to be a member a, if not it will not compile)


With the introduction of constexpr if(cond) in C++17 you can achieve your goal. constexpr if(cond) gets evaluated at compile time, hence you can choose what you want to do depending the type of the parameter. Following snippet provides an illustration.

#include <iostream>
#include <string>
#include <type_traits>

struct solo{
  int a;     
};

struct duo : solo{
    int b;
};

template<class T>
void function(T test){ 
 if constexpr (std::is_same<T, duo>::value) 
    std::cout<< std::to_string(test.b)<<"\n";
 else if constexpr (std::is_same<T, solo>::value) 
    std::cout<< std::to_string(test.a)<<"\n";
}

int main()
{
  solo test1;
  test1.a = 1;

  duo test2;
  test2.b = 2;

  function(test1);
  function(test2);
}

Tags:

C++

Templates