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

时间:2019-01-15 18:20:52

标签: 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.

3 个答案:

答案 0 :(得分:5)

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.

答案 1 :(得分:3)

  

没有超载

但是这里的重载使代码变得简单:

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;
}

答案 2 :(得分:2)

我建议将您的功能分为两部分。保留迭代部分doSomething并分离出您想要完成的实际操作。

template<typename T> void theThing(T one);

template<>
void theThing<MyClass_one>(MyClass_one one) {
    cout << "im one" << endl;
}

template<>
void theThing<MyClass_two>(MyClass_two one) {
    cout << "im two" << endl;
}

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

这样,您可以为要使用的每个类提供专门的东西。值得一提的是,它不会针对theThing非专业类型进行编译。