partial specialization of function templates

六月ゝ 毕业季﹏ 提交于 2019-11-29 14:02:30

问题


In the below code snippet,

template<typename T1>
void func(T1& t)
{
    cout << "all" << endl;
}

template<typename T2>
void func(T2 &t)
{
    cout << "float" << endl;
}

// I do not want this
// template<> void func(float &t)

int main()
{
    int i; float f;
    func(i); // should print "all"
    func(f); // should print "float" 
    return 0;
}

I would like to have the templates modified which by passing any type other than float will print "all" and passing float will print "float". I do not want template specialization, instead have partial specialization which will act accordingly based on input type. How should i go about it. Thanks in advance.

Well the scenario, i'm currently facing is like, I need to have the following defined,

template<typename T1>
void func(T1 &t)
{
    cout << "t1" << endl;
}

template<typename T2>
void func(T2 &t)
{
    cout << "t2" << endl;
}

The following calls should print "t2"

func(int) // print "t2"
func(float) // print "t2"
func(string) // print "t2"

The following calls should print "t1"

func(char) // print "t1"
func(xyz) // print "t1"
...
func(abc) // print "t1"

some kind of grouping like the above where few should call the partial specialization implementation and others should call the default implementation.


回答1:


Write a type traits class for your condition:

template<class T>
struct IsIntFloatOrString {
  enum { value = boost::is_same<T, int>::value
              or boost::is_same<T, float>::value
              or boost::is_same<T, string>::value };
};

Use boost::enable_if and disable_if:

template<typename T1>
typename boost::enable_if<IsIntFloatOrString<T1> >::type
func(T1 &t) {
  cout << "t1" << endl;
}

template<typename T2>
typename boost::disable_if<IsIntFloatOrString<T2> >::type
func(T2 &t) {
  cout << "t2" << endl;
}



回答2:


You can combine function overloading with templates. So:

#include <iostream>

template<typename T>
void func(T& t)
{
    std::cout << "all" << std::endl;
}

void func(float& f)
{
    std::cout << "float" << std::endl;
}

int main()
{
    int i; float f;
    func(i); // prints "all"
    func(f); // prints "float" 
    return 0;
}



回答3:


You cannot partially specialise functions in C++.

Perhaps this is not the terminology you mean. You can use templates like boost::is_same<T1, T2> to perform conditional logic based on the given template parameter. You can also use T in any place where you'd use any other type, such as in typeid(T).name():

template <typename T>
void foo(T&) {
   if (boost::is_same<T, int>::value)
      std::cout << "int lol";
   else
      std::cout << typeid(T).name();
}

(Although I'd not recommend using typeid().name() as its value is not specified by the standard and can vary from the type written in your code, to a mangled symbol, or the lyrics to Pokerface.)

Addendum Like other answerers, I would personally choose template specialisation itself or just plain ol' function overloading. I don't know why you're averse to them, but that is what they are there for.




回答4:


As Tomalak already said in his answer you can not partially specialize a template function, but if you change your function to be a static member function in a template class, you could do it.

However, a better approach would be function overloading.




回答5:


This is how to make it work without ugly syntax a and !b and !c for enable_if in case of arbitrary number of conditions.

If we know that partial specialization don't work work function but work with classes, let's use classes! We should hide it from people, but we can use them!

OK, code:

#include <type_traits>
#include <iostream>


template <typename T>
class is_int_or_float : public std::integral_constant<bool, std::is_same<T, int>::value || std::is_same<T, float>::value> {
};


template<typename T, typename Enable = void> //(2)
struct Helper {
    static void go(const T&) {
                std::cout << "all"<< std::endl;
        }
};

template<typename T>
struct Helper<T, typename std::enable_if<is_int_or_float<T>::value>::type> { // (3)
        static void go(const T&) {
                std::cout << "int or float" << std::endl;
        }
};

template<typename T>
struct Helper<T, typename std::enable_if<std::is_pointer<T>::value>::type> { // (3)
        static void go(const T&) {
                std::cout << "pointer" << std::endl;
        }
};

template<typename T>
void func(const T& arg) {
        Helper<T>::go(arg); // (1)
}
int main() {
        char c;
        int i;
        float f; 
        int* p;
        func(c);
        func(i);
        func(f);
        func(p);
}

(1) First of all just for every type call helper. No specialization for functions.
(2) Here we add one dummy argument. We don't have to specify it on calling because it's default to void (3) In 3 we just give void, when we allow T and anything else (or SFINAE as in our case). One important thing is that we shouldn't allow some T twice or more.

Notes:

  1. We can also change default type to std::true_type, after that we will be able to get rid of std::enable_if (std::enable_if<some_trait<T>::value> will be change to just some_trait<T>::type). I'm not sure which

  2. This code uses type traits from C++11. If you don't have c++11 support you may write your own traits or use type traits from boost

Live example



来源:https://stackoverflow.com/questions/5002616/partial-specialization-of-function-templates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!