Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allowing template parameter to be only certain types and deciding action based on it

Tags:

c++

templates

Let say i have two classes MyClass_one , MyClass_two

And i have function that accept only them as first parameter

template<typename T,typename ...Ts>
void doSomething(T one, Ts...two){}

Now to make it simple, if parameter one is MyClass_one it should print "im one" if its MyClass_two it should print "im two".

How to actually achieve this? The only solution i have came up with is really ugly and does not containt compile error throwing:

template<typename T> isOne{ static const bool value = false}
template<> isOne<MyClass_one>{ static const bool value = true}

template<typename T> isTwo{ static const bool value = false}
template<> isTwo<MyClass_two>{ static const bool value = true}

template<typename T, typename ... Ts>
void doSomething(T one, Ts...two){
  if( isOne<T>::value ) { cout << "im one" << endl;}
  else if ( isTwo<T>::value){ cout <<"im two" << endl;}
}

However how to implement the compiler error check without overloading (multiple definition of doSomething() function) e.g the function will not compile if something else than MyClass_one or MyClass_two is passed.

thanks for help.

like image 294
Darlyn Avatar asked Jan 15 '19 18:01

Darlyn


2 Answers

If you can use C++17, you can use if constexpr:

template<typename T, typename ... Ts>
void doSomething(T one, Ts...two){
  if constexpr ( isOne<T>::value ) { cout << "im one" << endl;}
  else if constexpr ( isTwo<T>::value){ cout <<"im two" << endl;}
}

Of course, isOne<T>::value and isTwo<T>::value need to be static constexpr variables.

If you want to check the types of the first function argument, the same approach holds, only there is no need for something like isOne and isTwo, you can use std::is_same_v to see if the first argument is MyClassOne or MyClassTwo:

#include <iostream> 
#include <type_traits>
#include <vector> 

class MyClassOne {}; 
class MyClassTwo {}; 

template<typename T, typename ... Ts>
void doSomething(T one, Ts...two){
  if constexpr ( std::is_same_v<T, MyClassOne> ) 
    std::cout << "im one" << std::endl;
  else if constexpr ( std::is_same_v<T, MyClassTwo> )
    std::cout <<"im two" << std::endl;
  else
    static_assert(false, "Only MyClassOne and MyClassTwo are permitted first arguments.");
}
int                                                                                                                                         
main(int argc, char **argv) { 

    MyClassOne one; 
    MyClassTwo two; 

    doSomething(one, 1.5, two); 
    doSomething(two, 'c', one);

    std::vector<MyClassOne> onesVector;
    doSomething(onesVector, 1.0); 

}  

std::is_same_v<A,B> results in atrue value if the types A and B are the same. This answers your question "if parameter one is MyClass_one it should print "im one" if its MyClass_two it should print "im two".", and fails at compile time if the first argument is any type different than etither myClassOne or myClassTwo.

Edit: added a static_assert that makes sure the compilation fails if the first argument is anything else except MyClassOne or MyClassTwo, as suggested by Justin Time in the comment.

like image 171
tmaric Avatar answered Oct 20 '22 00:10

tmaric


without overloading

but overloading here make code simple:

template<typename ... Ts>
void doSomething(MyClass_one, Ts...two){
    cout << "im one" << endl;
}

template<typename ... Ts>
void doSomething(MyClass_two, Ts...two){
    cout <<"im two" << endl;
}
like image 22
Jarod42 Avatar answered Oct 20 '22 01:10

Jarod42