Writing a function template that is specialised for a class and its subclasses

岁酱吖の 提交于 2019-12-20 07:16:29

问题


I am trying to write a function template. One version should be used for all types that don't satisfy the criteria for the other version; the other version should be used when the argument is a base class of a given class, or that class itself.

I have tried doing an overload for Base&, but when classes are derived from Base, they use the general one, not the specific one.

I also have tried this SFINAE approach:

struct Base { };

struct Derived : public Base { };

struct Unrelated { };

template<typename T>
void f(const T& a, bool b = true) {
    cout << "not special" << endl;
}

template<typename T>
void f(const Base& t, bool b = is_base_of<Base, T>::value) {
    cout << "special" << endl;
}

Base b;
Derived d;
Unrelated u;

f(b); f(d); f(u);

But all of them print "not special". I am not good at SFINAE and I am probably just doing it wrong. How can I write a function like this?


回答1:


First, none of these will ever call the "special" f overload because T cannot be deduced from the function arguments. Its first parameter needs to be of type T:

void f(const T& t, bool b = is_base_of<Base, T>::value)

Once that is done, note that the "special" overload doesn't really use SFINAE to affect overload resolution: is_base_of<T, U>::value always has a value: it's either true or false. To affect overload resolution, you need to use enable_if, which conditionally defines a type based on a boolean value.

Also, both overloads need to use SFINAE: the "special" overload must be enabled if T is derived from the base (or is the base type), and the "not special" overload must be enabled only if T is not derived from the base, otherwise there will be overload resolution ambiguities.

The two overloads should be declared and defined as:

template<typename T>
void f(T const& a, typename enable_if<!is_base_of<Base, T>::value>::type* = 0)
{
    cout << "not special" << endl;
}

template<typename T>
void f(T const& t, typename enable_if<is_base_of<Base, T>::value>::type* = 0)
{
    cout << "special" << endl;
}

Finally, note that there is no specialization here. These two functions named f are overloads.




回答2:


Here's a simple C++03 approach:

namespace detail // implementation details, users never invoke these directly
{
    template<bool B>
    struct f_impl
    {
        template<typename T>
        static void f(T const& t) { std::cout << "not special\n"; }
    };

    template<>
    struct f_impl<true>
    {
        static void f(Base const& t) { std::cout << "special\n"; }
    };
}

template<typename T>
void f(T const& t)
{
    detail::f_impl<is_base_of<Base, T>::value>::f(t);
}

Live demo.




回答3:


One way to do it with overloading would be like this:

#include <iostream>

using namespace std;

struct Base { };

struct Derived : public Base { };

struct Unrelated { };

void f(...) {
    cout << "not special" << endl;
}

void f(const Base& t) {
    cout << "special" << endl;
}

int main(){ 
    Base b;
    Derived d;
    Unrelated u;

    f(b); 
    f(d);
    f(u);

    return 0;
}

Result:

special
special
not special

An overload taking a variable argument list will take any type of argument, but is always considered less suitable than any other overload that works at all.



来源:https://stackoverflow.com/questions/10401676/writing-a-function-template-that-is-specialised-for-a-class-and-its-subclasses

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