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?
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.
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.
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.
Related
I have created a template function, defined below.
template<class T>
void func(T t){ /* do stuff */ }
I would like to overload this template in the event that T inherits from an abstract class I made.
class A {
public:
virtual void doStuff() = 0;
};
class B : public A {
virtual void doStuff(){ /* do stuff */ }
}
I have tried using template specialization (below), but it still was using the original definition.
template<>
void func(A& a){ /* do different stuff */ } // Not called by func(B())
I tried overloading it as well, and while this worked with ints, it isn't working with my base class.
func(int i){ /* do stuff with i */ } // Called by func(3)
func(A& a){ /* do different stuff */ } // Not called by func(B())
I'm guessing this has to do with C++ not wanting to implicitly cast my instance of B to an A and reference it, but I haven't been able to find anything explaining how I can fix this behavior. Since A has a pure virtual function, I can't just define func(A a). Any help would be appreciated.
Here is an example in which the behavior I'm experiencing can be reproduced.
#include <iostream>
template<class T>
void func(T t){
std::cout << "Template function called!" << std::endl;
}
class A {
public:
virtual void doStuff() = 0;
};
class B : public A{
public:
virtual void doStuff(){};
};
template<>
void func(const A& a){
std::cout << "Specialized template called!" << std::endl;
}
void func(const A& a){
std::cout << "Overload called!" << std::endl;
}
int main(){
B b{};
func(b);
return 0;
}
If you have access to C++17, then you can create a public overload that will forward to the correct function:
namespace detail {
template<class T>
void func(T t) {
std::cout << "Template function called!" << std::endl;
}
void func(A& a){
std::cout << "Overload called!" << std::endl;
}
}
template<class T>
void func(T& t) {
if constexpr (std::is_base_of_v<A, T>) {
detail::func(static_cast<A&>(t));
} else {
detail::func(t);
}
}
int main() {
B b;
func(b);
}
Otherwise you can use tag dispatching or SFINAE:
Tag dispatching:
template<class T>
void func(T t, std::false_type) {
std::cout << "Template function called!" << std::endl;
}
void func(A& a, std::true_type) {
std::cout << "Other function called!" << std::endl;
}
template<class T>
void func(T& t) {
func(t, std::is_base_of<A, T>{});
}
SFINAE:
template<class T,
std::enable_if_t<std::is_base_of_v<A, T>>* = nullptr>
void func(T t) {
std::cout << "Template function called!" << std::endl;
}
template<class T,
std::enable_if_t<!std::is_base_of_v<A, T>>* = nullptr>
void func(T& t) {
std::cout << "Other function called!" << std::endl;
}
C++20 solution
You can run the code here.
Using Concepts from C++20, we can write an inherits_from concept, and use that. Concepts allow us to constrain a template so that it only applies in situations where an expression is true.
The concept looks like this:
#include <type_traits>
template<class Derived, class Base>
concept derived_from = std::is_base_of_v<Base, Derived>;
Then, we can write the generic template and the constrained template:
struct MyBase{};
struct MyDerived : MyBase{};
// This is the generic template; using auto here is valid in C++20
void do_thing(auto const& thing) {
std::cout << "Doing thing on regular type\n";
}
//This is the template that acts on classes derived from MyBase
void do_thing(derived_from<MyBase> const& x) {
std::cout << "Doing thing on MyBase\n";
}
Because the second function declares T as following the concept inherits_from, it's more specialized, so for types that actually inherit from MyBase, it'll be selected over the generic template:
int main() {
do_thing(10); // Prints "Doing thing on regular type"
do_thing(MyBase()); // Prints "Doing thing on MyBase"
do_thing(MyDerived()); // Prints "Doing thing on MyBase"
}
C++17 solution
You can run the code here.
We can emulate the behavior of concepts using SFINAE, although this requires modifying the generic template so that it'll by ignored if T extends MyBase. The key to using SFINAE is to trigger a substitution failure if a condition is false, resulting in that overload being ignored.
In order to trigger a substitution failure, add a defaulted template argument at the end of the list of template parameters. In our case, it looks like this:
template<
class T,
// This defaulted argument triggers the substitution failure
class = std::enable_if_v</* condition */>>
In our code,
- The generic overload will be disabled if T extends MyBase
- The constrained overload will be disable if T doesn't extend MyBase
It looks like this:
struct MyBase {};
struct MyDerived : MyBase {};
template<
class T,
class = std::enable_if_t<!std::is_base_of_v<MyBase, T>>>
void do_thing(T const&) {
std::cout << "Doing thing on regular type\n";
}
// This overload is *disabled* if T doesn't inherit from MyBase
template<
class T,
// We have to have an additional defaulted template argument to distinguish between the overloads
class = void,
class = std::enable_if_t<std::is_base_of_v<MyBase, T>>>
void do_thing(T const& x) {
std::cout << "Doing thing on MyBase\n";
}
Despite the weird declaration, we can still use do_thing as though it were a regular function:
int main() {
do_thing(10);
do_thing(MyBase());
do_thing(MyDerived());
}
Backporting things to C++11
You can run the code here.
We only have to make a few minor changes to backport things to C++11. Basically,
is_base_of_v<MyBase, T> has to be replaced by is_base_of<MyBase, T>::value, and
enable_if_t</* condition */> has to be replaced by typename enable_if</* condition */>::type
B b;
func((A&)b);
I don't think you can pass a temporary here. You cannot cast B() to 'A&' because it is an rvalue and casting it to const A& makes the template version a better match.
Another options (on top of other answers) using c++11 - explicitly removing the template version if T is a subtype of B:
template<class T>
typename std::enable_if<!std::is_base_of<A, T>::value>::type
func(T& t){
std::cout << "Template function called!" << std::endl;
}
void func(const A& a){
std::cout << "Overload called!" << std::endl;
}
int main(){
func(B());
}
I want to get into more template meta-programming. I know that SFINAE stands for "substitution failure is not an error." But can someone show me a good use for SFINAE?
I like using SFINAE to check boolean conditions.
template<int I> void div(char(*)[I % 2 == 0] = 0) {
/* this is taken when I is even */
}
template<int I> void div(char(*)[I % 2 == 1] = 0) {
/* this is taken when I is odd */
}
It can be quite useful. For example, i used it to check whether an initializer list collected using operator comma is no longer than a fixed size
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}
The list is only accepted when M is smaller than N, which means that the initializer list has not too many elements.
The syntax char(*)[C] means: Pointer to an array with element type char and size C. If C is false (0 here), then we get the invalid type char(*)[0], pointer to a zero sized array: SFINAE makes it so that the template will be ignored then.
Expressed with boost::enable_if, that looks like this
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i,
typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}
In practice, i often find the ability to check conditions a useful ability.
Heres one example (from here):
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
// Will be chosen if T is anything except a class.
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
When IsClassT<int>::Yes is evaluated, 0 cannot be converted to int int::* because int is not a class, so it can't have a member pointer. If SFINAE didn't exist, then you would get a compiler error, something like '0 cannot be converted to member pointer for non-class type int'. Instead, it just uses the ... form which returns Two, and thus evaluates to false, int is not a class type.
In C++11 SFINAE tests have become much prettier. Here are a few examples of common uses:
Pick a function overload depending on traits
template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
//integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
//floating point version
}
Using a so called type sink idiom you can do pretty arbitrary tests on a type like checking if it has a member and if that member is of a certain type
//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;
//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};
struct S{
int bar;
};
struct K{
};
template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
std::cout << "has bar" << std::endl;
}
void print(...){
std::cout << "no bar" << std::endl;
}
int main(){
print(S{});
print(K{});
std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}
Here is a live example: http://ideone.com/dHhyHE
I also recently wrote a whole section on SFINAE and tag dispatch in my blog (shameless plug but relevant) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html
Note as of C++14 there is a std::void_t which is essentially the same as my TypeSink here.
Boost's enable_if library offers a nice clean interface for using SFINAE. One of my favorite usage examples is in the Boost.Iterator library. SFINAE is used to enable iterator type conversions.
Here's another (late) SFINAE example, based on Greg Rogers's answer:
template<typename T>
class IsClassT {
template<typename C> static bool test(int C::*) {return true;}
template<typename C> static bool test(...) {return false;}
public:
static bool value;
};
template<typename T>
bool IsClassT<T>::value=IsClassT<T>::test<T>(0);
In this way, you can check the value's value to see whether T is a class or not:
int main(void) {
std::cout << IsClassT<std::string>::value << std::endl; // true
std::cout << IsClassT<int>::value << std::endl; // false
return 0;
}
Examples provided by other answers seems to me more complicated than needed.
Here is the slightly easier to understand example from cppreference :
#include <iostream>
// this overload is always in the set of overloads
// ellipsis parameter has the lowest ranking for overload resolution
void test(...)
{
std::cout << "Catch-all overload called\n";
}
// this overload is added to the set of overloads if
// C is a reference-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)(c.*f)(), void())
{
std::cout << "Reference overload called\n";
}
// this overload is added to the set of overloads if
// C is a pointer-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)((c->*f)()), void())
{
std::cout << "Pointer overload called\n";
}
struct X { void f() {} };
int main(){
X x;
test( x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
Output:
Reference overload called
Pointer overload called
Catch-all overload called
As you can see, in the third call of test, substitution fails without errors.
C++17 will probably provide a generic means to query for features. See N4502 for details, but as a self-contained example consider the following.
This part is the constant part, put it in a header.
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
The following example, taken from N4502, shows the usage:
// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())
// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;
Compared to the other implementations, this one is fairly simple: a reduced set of tools (void_t and detect) suffices. Besides, it was reported (see N4502) that it is measurably more efficient (compile-time and compiler memory consumption) than previous approaches.
Here is a live example, which includes portability tweaks for GCC pre 5.1.
Here is one good article of SFINAE: An introduction to C++'s SFINAE concept: compile-time introspection of a class member.
Summary it as following:
/*
The compiler will try this overload since it's less generic than the variadic.
T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);
int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.
It simply tries the next overload.
*/
template <typename T> void f(const T& t, typename T::iterator* it = nullptr) { }
// The sink-hole.
void f(...) { }
f(1); // Calls void f(...) { }
template<bool B, class T = void> // Default template version.
struct enable_if {}; // This struct doesn't define "type" and the substitution will fail if you try to access it.
template<class T> // A specialisation used if the expression is true.
struct enable_if<true, T> { typedef T type; }; // This struct do have a "type" and won't fail on access.
template <class T> typename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return obj.serialize();
}
template <class T> typename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return to_string(obj);
}
declval is an utility that gives you a "fake reference" to an object of a type that couldn't be easily construct. declval is really handy for our SFINAE constructions.
struct Default {
int foo() const {return 1;}
};
struct NonDefault {
NonDefault(const NonDefault&) {}
int foo() const {return 1;}
};
int main()
{
decltype(Default().foo()) n1 = 1; // int n1
// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
std::cout << "n2 = " << n2 << '\n';
}
The following code uses SFINAE to let compiler select an overload based on whether a type has certain method or not:
#include <iostream>
template<typename T>
void do_something(const T& value, decltype(value.get_int()) = 0) {
std::cout << "Int: " << value.get_int() << std::endl;
}
template<typename T>
void do_something(const T& value, decltype(value.get_float()) = 0) {
std::cout << "Float: " << value.get_float() << std::endl;
}
struct FloatItem {
float get_float() const {
return 1.0f;
}
};
struct IntItem {
int get_int() const {
return -1;
}
};
struct UniversalItem : public IntItem, public FloatItem {};
int main() {
do_something(FloatItem{});
do_something(IntItem{});
// the following fails because template substitution
// leads to ambiguity
// do_something(UniversalItem{});
return 0;
}
Output:
Float: 1
Int: -1
Here, I am using template function overloading (not directly SFINAE) to determine whether a pointer is a function or member class pointer: (Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true?)
https://godbolt.org/z/c2NmzR
#include<iostream>
template<typename Return, typename... Args>
constexpr bool is_function_pointer(Return(*pointer)(Args...)) {
return true;
}
template<typename Return, typename ClassType, typename... Args>
constexpr bool is_function_pointer(Return(ClassType::*pointer)(Args...)) {
return true;
}
template<typename... Args>
constexpr bool is_function_pointer(Args...) {
return false;
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main(void) {
int* var;
std::cout << std::boolalpha;
std::cout << "0. " << is_function_pointer(var) << std::endl;
std::cout << "1. " << is_function_pointer(fun_void_void) << std::endl;
std::cout << "2. " << is_function_pointer(fun_void_double) << std::endl;
std::cout << "3. " << is_function_pointer(fun_double_double) << std::endl;
std::cout << "4. " << is_function_pointer(&test_debugger::var) << std::endl;
return 0;
}
Prints
0. false
1. true
2. true
3. true
4. true
As the code is, it could (depending on the compiler "good" will) generate a run time call to a function which will return true or false. If you would like to force the is_function_pointer(var) to evaluate at compile type (no function calls performed at run time), you can use the constexpr variable trick:
constexpr bool ispointer = is_function_pointer(var);
std::cout << "ispointer " << ispointer << std::endl;
By the C++ standard, all constexpr variables are guaranteed to be evaluated at compile time (Computing length of a C string at compile time. Is this really a constexpr?).
I am kind of C++ newbie, especially when dealing with templates. I have a template class "Foo" that is intended to take different structures as template parameters. I need also to have a member function of the class that works differently depending on the type template parameter, so I specialise such a function. The general picture would be as follows
struct A
{
float paramOfA;
};
struct B
{
float paramOfB;
};
template <typename T>
class Foo
{
public:
void doSomethingOnType(T& arg);
//...more functions and stuff...
};
// function specialisation for A's
template<> void Foo<A>::doSomethingOnType(A& a){
//do something on member of A
a.paramOfA = ...;
std::cout<< "I've done something on a's member..."<<std::endl;
}
// function specialisation for B's
template<> void Foo<B>::doSomethingOnType(B& b){
//do something on member of B
b.paramOfB = ...;
std::cout<< "I've done something on b's member..."<<std::endl;
}
So far so good, right? Imagine that now I have a structure C that derives from B:
struct C:B
{
float paramOfC;
};
Now, when I instantiate a Foo object that takes C structure template type, I would want the function "doSomethingOnType" to keep the same behaviour of the function for B types on C's member that derives from B (paramOfB), eventhough I haven't specialised such a function for C structure types. For instance
Foo<C> o;
C oneC;
o.doSomethingOnType(oneC);
I am sure that when executing the above piece of code, the function will take any implementation given in the templated class, not in the specialised version for B. But I really want to keep the latter implementation of the function when using C types since, being C derived from B, it would make a lot of sense to me as well as saving me time from having to write more lines of code for a function specialisation for C's that has the same behaviour than for B (imagine that B has 50 members instead of a single one). Is it possible to do so without, as I said, specialising the function for C structure types?
Thanks a lot in advance for any help!
Really excited to ask me first question in stackoverflow :-)
You can do some hook thing in general template function, though I hate it....
these code can as following:
template <typename T>
class Foo {
public:
void doSomethingOnType(T& arg)
{
if (dynamic_cast<B*>(&arg) != NULL) {
Foo<B> fb;
fb.doSomethingOnType(arg);
} else {
std::cout << "I've done something on Foo's member..." << std::endl;
}
}
};
Although How do you force a templatization to match a base class? is an elegant solution if your function takes an instance of T as a parameter, I would like to introduce several ways if it does not:
TestCase
class A1 {};
class A2:public A1 {};
class B{};
class C{};
Wrapped Function
This might be the simplest solution:
template<typename T>
class Bar
{
public:
void fun()
{
fun_impl((T*)(0));
}
void fun_impl( A1* const)
{
cout << "A1" << endl;
}
void fun_impl(B* const)
{
cout << "B" << endl;
}
void fun_impl(void*)
{
cout << "Neither A nor B" << endl;
}
};
Bar<A2>().fun(); // A1
Bar<B>().fun(); // B
Bar<C>().fun(); // Neither A nor B
Because the precedence of fun_impl which exactly matches(or as an accessible base class of) the type > those which requires void* conversion, the correct version will be enabled.
(NOTE: This is true on clang3.7 and gcc5.3, but I din't refer to standard)
However, if we haveclass A3: private A1, an error will be raised during the compilation of Bar<A3>().fun().
The following two solutions require C++11:
Partial Specialization of Class Template
template<typename T, bool = std::is_base_of<A1, T>::value,
bool = std::is_base_of::value >
struct Foo
{
void fun();
}; //Main Version
template<typename T>
struct Foo<T,false,false>
{
void fun();
}; //Specialization of Neither A nor B
template<typename T>
void Foo<T,false,false>::fun()
{
cout << "neither A nor B" << endl;
}
template<typename T>
struct Foo<T,true,false>
{
void fun();
}; //Specialization of A
template<typename T>
void Foo<T,true,false>::fun()
{
cout << "A" << endl;
}
template<typename T>
struct Foo<T, false, true>
{
void fun();
}; //Specialization of B
template<typename T>
void Foo<T,false,true>::fun()
{
cout << "B" << endl;
}
Foo<A2>().fun(); //A
Foo<B>().fun(); //B
Foo<C>().fun(); //Neither A nor B
Foo<APrivate>().fun(); //A
SFINAE
If you don't want to specialize the whole class, maybe SFINAE in one class could be the best choice:
namespace
{
class Helper1 {};
class Helper2 {};
} // Helper classes to avoid ambiguity
template<typename T>
class Foo
{
public:
template<typename TIn= T, typename U= typename std::enable_if<std::is_base_of<A1, TIn>::value>::type >
void fun(Helper1 = Helper1())
{
cout << "A" << endl;
}
template<typename TIn=T ,typename U = typename std::enable_if<std::is_base_of<B, TIn>::value>::type >
void fun(Helper2 = Helper2())
{
cout << "B" << endl;
}
template<typename TIn = T, typename = typename std::enable_if<!std::is_base_of<A1,TIn>::value>::type ,
typename = typename std::enable_if<!std::is_base_of<B,TIn>::value>::type >
void fun()
{
cout << "Neither A nor B" << endl;
}
};
In the case above a function will be instantiated only if it matches some certain conditions. Since three void fun() are not allowed in one class, helper classes are required.
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?
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.
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.
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.
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.
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;
}
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;
}
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.
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.
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:
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
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