问题
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