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());
}
Related
I want to define a template class IE which provides public method(). method() calls underlying private run(), which may take arguments related to template parameters passed to IE. So:
For IE<void>, run() without arguments is executed.
For any other T in IE<T>, run(vector<T>) is executed.
I think I correctly SFINAE'd method run(), but I have problem with defining the parameter that should be passed to run. I came up with defining Extra in the presented way, but I get errors that T can't be deduced.
EDIT: I need a solution working for C++14 at most.
template<typename X=void>
class IE
{
template<typename T=void>
struct Extra;
template<typename T>
struct Extra<enable_if_t<is_void<T>::value, T>> {};
template<typename T>
struct Extra<enable_if_t<!is_void<T>::value, T>>
{
std::vector<T> ex;
};
template<typename X_=X>
void run(enable_if_t<is_void<X_>::value , Extra<X_>> x) {
cout << "In run" << endl;
}
template<typename X_ = X>
void run(enable_if_t<!is_void<X_>::value , Extra<X_>> x)
{
cout << "In run: X=" << x.ex.size() << endl;
}
public:
void method()
{
Extra<X> x;
run(x);
}
};
int main() {
IE<double> ie1;
ie1.method(); // should execute run(vector<double>)
IE<> ie2;
ie2.method(); // should execute run()
return 0;
}
From your intent, you can do it with Constexpr if (since C++17). e.g.
template<typename X=void>
class IE
{
void run() {
cout << "In run()" << endl;
}
void run(std::vector<X> x)
{
cout << "In run: X=" << x.size() << endl;
}
public:
template<typename X_ = X>
void method()
{
if constexpr (std::is_same_v<X_, void>)
run();
else
run(std::vector<X_>{...});
}
};
LIVE
Before C++17 you can apply SFINAE (or specialization), e.g.
template<typename X=void>
class IE
{
void run() {
cout << "In run()" << endl;
}
void run(std::vector<X> x)
{
cout << "In run: X=" << x.size() << endl;
}
public:
template<typename X_ = X>
std::enable_if_t<std::is_same<X_, void>::value> method()
{
run();
}
template<typename X_ = X>
std::enable_if_t<!std::is_same<X_, void>::value> method()
{
run(std::vector<X_>{...});
}
};
LIVE
For your original solution, you should apply SFINAE as
template<typename X=void>
class IE
{
template<typename T, typename = void>
struct Extra;
template<typename T>
struct Extra<T, enable_if_t<is_void<T>::value>> {};
template<typename T>
struct Extra<T, enable_if_t<!is_void<T>::value>>
{
std::vector<T> ex;
};
template<typename X_ = X>
enable_if_t<is_void<X_>::value> run(Extra<X_> x) {
cout << "In run" << endl;
}
template<typename X_ = X>
enable_if_t<!is_void<X_>::value> run(Extra<X_> x)
{
cout << "In run: X=" << x.ex.size() << endl;
}
public:
void method()
{
Extra<X> x;
run(x);
}
};
LIVE
It is your definition of Extra which is incorrect,
You might simply move it outside class and use regular specialization:
template<typename T>
struct Extra
{
std::vector<T> ex;
};
template<>
struct Extra<void> {};
Demo
It may be that the actual code is more complicated, but for the code presented in the question, providing a specialization for IE<void> is the simplest approach. Get rid of all those enable_if things, and after definition of IE add the specialization:
template <>
class IE<void> {
void run() { /* ... */ }
public:
void method() { run(); }
};
(Yes, I'm not a fan of using constexpr if to write functions that have two or more completely independent execution paths depending on some type calculus; that's far too much like #ifdef ... #elif ... #endif)
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.
How can I specialize or overload function func so that specialization handles all instances of MyClass? Assume func is a librarian function (like std::swap for example) so I can't change func, and I can't replace or wrap it, I have to specialize it.
#include <iostream>
template<typename T>
class MyClass
{
};
template<typename T>
void func(const T&)
{
std::cout << "Default" << std::endl;
}
// I don't want to copy this stuff for every instance of MyClass
template<>
void func<MyClass<int>>(const MyClass<int>&)
{
std::cout << "Specialization" << std::endl;
}
int main(int, char**)
{
func(int(0)); // "Default"
func(MyClass<int>()); // "Specialization"
func(MyClass<double>()); // "Default" but I want "Specialization" here
return 0;
}
Here are a few examples of what you can do.
template<typename T>
void func(const T&)
{
std::cout << "Default" << std::endl;
}
template<template<typename> class A , typename B>
void func(const A<B>&)
{
std::cout << "Overload All templated classes with one templated argument B" << std::endl;
}
template<typename T>
void func(const MyClass<T>&)
{
std::cout << "Overload Myclass with some templated argument T" << std::endl;
}
If you cannot change the global func in anyway and still want to avoid copying one way is to use macro to automate the repeatative stuff for you.
#define Specialize(TYPE) \
template<> \
void func<MyClass<TYPE>>(const MyClass<TYPE>&) \
{ \
std::cout << "Specialization" << std::endl; \
}
Specialize(int);
Specialize(double);
If you have freedom to alter MyClass<T>, then place all the common functionality of MyClass<T> into a common non-template base class:
template<typename T>
class MyClass<T> : public MyClassBase // <---- a common base class
...
And simply overload or specialize func() only for MyClassBase:
void func(const MyClassBase&)
{
std::cout << "Specialization" << std::endl;
}
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