Select class template specialised for base-class of derived instance - c++

Consider the following example program:
#include <iostream>
template<typename T>
struct Dispatch
{
static void send(T&) { std::cout << "unknown\n"; }
};
struct Processor
{
template<typename T>
void process(T&& t) { Dispatch<T>::send(t); }
};
template<typename T>
struct Base
{};
template<typename T>
struct Dispatch<Base<T>>
{
static void send(Base<T>&) { std::cout << "base\n"; }
};
struct Deriv : Base<int>
{};
int main()
{
Processor p;
p.process(Base<int>{}); // prints "base"
p.process(Deriv{}); // prints "unknown"
return 0;
}
When calling Processor::process() with a Deriv instance (which is a Base subclass), I would like the Dispatcher() specialised for Base class templates to be selected.
However, in the example above, the following occurs:
passing a Base<T> instance to process() calls the Base<T> specialisation
passing a Deriv instance to process() calls the primary class template
Question:
Why is Base<T> not a better specialisation for Deriv than the primary class template?
Is there a general way to call the Base<T> specialisation when passed Base<T> subclasses?

With SFINAE, you may do:
// Traits to detect inheritance:
template <typename T> std::true_type derive_from_base_impl(Base<T>*);
std::false_type derive_from_base_impl(...);
template <typename T>
using derive_from_base_t = decltype(derive_from_base_impl(std::declval<T*>()));
Then some changes
template<typename T, typename Enabler = void>
struct Dispatch
{
static void send(T&) { std::cout << "unknown\n"; }
};
template<typename T>
struct Dispatch<T, std::enable_if_t<derive_from_base_t<T>::value>>
{
static void send(T&) { std::cout << "base\n"; }
};
Demo

Template specialization works for literally the same type. If you specialized template for Base, this specialization will not work for Derived.
One way to solve this would be to use partial specialization which you enable for types derived from Base.

Related

How do I specialize member functions based on the base class of the templated class

I'm trying to specialize member functions of a template class based on the type of template. In particular I'd like to have specializations based on polymorphic types. I've been struggling with the syntax. Here is my try which obviously produces the error: two or more data types in declaration of doSomething()
class Base {};
class Derived : public Base {};
template<typename T>
class MyClass
{
public:
void doSomething();
};
template<>
template<typename T>
typename std::enable_if<std::is_base_of<Derived, T>::value>::type
void MyClass<T>::doSomething()
{
// Do something with Derived type
}
template<>
template<typename T>
typename std::enable_if<std::is_base_of<Base, T>::value &&
!std::is_base_of<Derived, T>::value>::type
void MyClass<T>::doSomething()
{
// So something with Base type
}
template<>
template<typename T>
typename std::enable_if<!std::is_base_of<Derived, T>::value>::type
void MyClass<T>::doSomething()
{
// Do something with all other types
}
Compilation gives..
error: two or more data types in declaration of 'doSomething'
BTW, I did get the following to compile, but the specialization did not work as expected at runtime. Base and derived types end up going through the non-specialized version of doSomething().
class Base {};
class Derived : public base {};
template<typename T>
class MyClass
{
public:
void doSomething()
{
// Do something for non-specialized types
}
};
template<>
void MyClass<Derived>::doSomething()
{
// Do something with Derived type
}
template<>
void MyClass<Base>::doSomething()
{
// So something with Base type
}
What would be the correct syntax?
You cannot specialize doSomething simply because it's not a template. MyClass is a template and you can specialize the class, each specialization having one doSomething. If that's not what you want then you need to make doSomething template overloads and, for the SFINAE to work, the SFINAE check must be done on the doSomething template parameter, not on the MyClass parameter. Lastly your checks are wrong.
So here is my version:
template<class T> struct MyClass
{
template <class U = T>
auto foo() -> std::enable_if_t<std::is_base_of_v<Base, U>
&& !std::is_base_of_v<Derived, U>>
{
foo_base();
}
template <class U = T>
auto foo() -> std::enable_if_t<std::is_base_of_v<Derived, U>>
{
foo_derived();
}
template <class U = T>
auto foo() -> std::enable_if_t<!std::is_base_of_v<Base, U>>
{
foo_else();
}
};
And here is a battery of tests:
class Base {};
class Derived : public Base {};
class A : Base {};
class B : Derived {};
class X {};
auto test()
{
MyClass<Base>{}.foo(); // foo_base
MyClass<Derived>{}.foo(); // foo_derived
MyClass<A>{}.foo(); // foo_base
MyClass<B>{}.foo(); // foo_derived
MyClass<X>{}.foo(); // foo_else
}
And of course I must mention the C++17 clean solution:
template<class T> struct MyClass
{
auto foo()
{
if constexpr (std::is_base_of_v<Derived, T>)
foo_derived();
else if constexpr (std::is_base_of_v<Base, T>)
foo_base();
else
foo_else();
}
};
Another possible solution pass through a ForFoo template class, that define a foo() method, with a couple of specializations for Base only and Derived classes. So MyClass<T> can inherit from ForFoo<T>.
I mean... if you define a ForFoo set of template classes as follows
template <typename T, typename = void>
struct ForFoo
{ void foo () { std::cout << "other type" << std::endl; } };
template <typename T>
struct ForFoo<T,
typename std::enable_if<std::is_base_of<Base, T>::value
&& ! std::is_base_of<Derived, T>::value>::type>
{ void foo () { std::cout << "Base type" << std::endl; } };
template <typename T>
struct ForFoo<T,
typename std::enable_if<std::is_base_of<Derived, T>::value>::type>
{ void foo () { std::cout << "Derived type" << std::endl; } };
MyClass simply become
template <typename T>
struct MyClass : public ForFoo<T>
{ };
The following is a full working C++11 example
#include <iostream>
#include <type_traits>
class Base {};
class Derived : public Base {};
class A : Base {};
class B : Derived {};
class X {};
template <typename T, typename = void>
struct ForFoo
{ void foo () { std::cout << "other type" << std::endl; } };
template <typename T>
struct ForFoo<T,
typename std::enable_if<std::is_base_of<Base, T>::value
&& ! std::is_base_of<Derived, T>::value>::type>
{ void foo () { std::cout << "Base type" << std::endl; } };
template <typename T>
struct ForFoo<T,
typename std::enable_if<std::is_base_of<Derived, T>::value>::type>
{ void foo () { std::cout << "Derived type" << std::endl; } };
template <typename T>
struct MyClass : public ForFoo<T>
{ };
int main ()
{
MyClass<Base>{}.foo(); // Base
MyClass<Derived>{}.foo(); // Derived
MyClass<A>{}.foo(); // Base
MyClass<B>{}.foo(); // Derived
MyClass<X>{}.foo(); // other
}

Exclude class member by specialization

I have base class template, on that I'm making some another class, which are specialized versions of my base class. I know how to exclude some methods from particular specialization case, but is there any possibility to make that for class's members? Example code, of what I want to achieve:
template<typename T>
class Base {
This variable exists only when T==integer VARIABLE;
}
template <Typename T>
WithVariable = using Base<int>;
template <Typename T>
Without = using Base<double>
I think that I should use std::enable in some way, but using it makes possible to make VARIABLE of type "void", when I don't want that variable. That is still not the situation I want to achieve.
Can you just specialise the class without the unwanted members?
template <typename T>
class Base {
public:
T t;
};
template <>
class Base<int> {
};
int main() {
Base<int> foo;
foo.t = 42; // Error: no member named 't' in 'Base<int>'
}
One possible way to do this is to inherit conditionally. Take a look:
struct BaseWithVariable {
int n {42};
};
struct BaseNoVariable {};
template <typename T>
struct Base : std::conditional<std::is_same<T, int>::value, BaseWithVariable, BaseNoVariable>::type {};
and usage:
Base<int> b {};
Base<double> d {};
std::cout << b.n << std::endl;
std::cout << d.n << std::endl; // error
You can also write an explicit specialization for your type, but if you have a group of types (like integers or type derived from specific base) which should have different members than general type, this solution can be helpful.
You can use a base for base that use SFINAE
#include <type_traits>
template <typename, typename = void>
struct base_of_base;
template <typename T>
struct base_of_base<T, typename std::enable_if<std::is_integral<T>::value>::type>
{ int var; };
template <typename T>
struct base_of_base<T, typename std::enable_if<!std::is_integral<T>::value>::type>
{ };
template <typename T>
class base : public base_of_base<T>
{ };
int main()
{
base<int> bi;
base<long> bl;
base<float> bf;
base<double> bd;
bi.var = 1; // OK
bl.var = 1; // OK
// bf.var = 1; // error: ‘class base<float>’ has no member named ‘var’
// bd.var = 1; // error: ‘class base<double>’ has no member named ‘var’
}
How about deleteing the functions from specialisations of the class?
template <typename T>
class Base {
public:
void notUseful();
};
template<typename T>
void Base<T>::notUseful(){
std::cout << "not useful" << std::endl;
return;
}
template <>
void Base<int>::notUseful() = delete;

How do I declare a method only if a template parameter of the class passes some test?

Having read the cppreference docs on std::enable_if, I know I can do something like this:
// foo2 overload is enabled via a parameter
template<class T>
T foo2(T t, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
return t;
}
But what if the template parameter being tested is from the class' template, not that of the method?
In the following example, I'm trying to overload a base class method if some template argument of the derived class is a floating point type:
class Base
{
public:
template <typename X>
void fun() {}
};
template <typename T>
class Derived : public Base
{
public:
template <typename X, typename std::enable_if<std::is_floating_point<T>::value, void>::type* = nullptr>
void fun() {}
};
int main()
{
Derived<double> d1;
Derived<int> d2;
return 0;
}
How do I make this work? Currently, I'm getting:
error: no type named ‘type’ in ‘struct std::enable_if’
You'd probably want to use partial specializations for this. Using enable_if on the template member function itself (which can be done, just add another template parameter with a default value of T and use enable_if on that parameter) will still hide the base class function.
class Base
{
public:
template <typename X>
void fun() { std::cout << "Base fun()" << std::endl; }
};
template <typename T, bool is_float = std::is_floating_point<T>::value>
class Derived : public Base
{
public:
template <typename X>
void fun() { std::cout << "Derived fun()" << std::endl;}
};
template <typename T>
class Derived<T, false> : public Base
{
};
Demo.

Is it possible to check at compile time whether a type is derived from some instantiation of a template?

I would like to write a template function which behaves one way if the passed type is derived from any template instantiation of another class, and another way if not.
I think the code below captures what I would like to do. Unfortunately Caller prints "generic" for both double and Derived.
#include <iostream>
template <typename T>
struct Base
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
void Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
(Note that Caller doesn't know which instantiation of Base that its parameter might derive from.)
It's calling the const T& overload because its a better match than const base<T>&. The reason is because calling the first requires no conversions and the second requires a derived-to-base conversion.
Here's a quick hack that shows you how it can be done (note the introduced base class):
#include <iostream>
#include <type_traits>
struct EvenMoreBase {};
template <typename T>
struct Base : EvenMoreBase
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
typename std::enable_if<!std::is_base_of<EvenMoreBase, T>::value>::type
Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
If you're able to use C++11 (or <type_traits> in general), the following is also a possible solution and covers not only types T : Base<T>, i.e. instances of the CRTP, but also T : Base<U> without another base class, as requested in your example.
#include <iostream>
#include <type_traits>
template <typename T>
struct Base
{
typedef T base_value_type;
};
struct Derived : public Base<int>
{
};
template <typename T, typename = T>
struct IsDerived
{
static const bool value = false;
};
template <typename T>
struct IsDerived<T, typename std::enable_if<std::is_base_of<Base<typename T::base_value_type>, T>::value, T>::type>
{
static const bool value = true;
};
template <typename T>
void Caller(const T&)
{
std::cout << IsDerived<T>::value << std::endl;
}
int main()
{
Caller(double()); // false
Caller(Derived()); // true
return 0;
}
Note the typedef T base_value_type - which might be called whatever your like. The idea is that each type T derived from Base<U> can leverage the knowledge of the base's template parameter. It doesn't matter if T == U or not. Trying to substitute the second parameter will fail as soon as you pass in a T that has no typedef T base_value_type and thus no specialization for this particular T will be generated.
EDIT: After processing your comment, and inspired by the thread I posted, I tried to somehow extract some base parameter U when examining some time type T : Base<U>. I don't think this can be done in the way you want, i.e. you pass whatever T and you extract U. However, you can do two things.
Simple Solution: If you have control over how derived classes are implemented, instead of adding a typedef in the base class, simply add a corresponding typedef in the derived class:
template <typename BaseParamType>
class Derived : public Base<BaseParamType>
{
public:
typedef BaseParamType base_param_type;
}
or, if you don't want derived classes to be class templates as well, simply hard code the type right into the type (you already know the type of the base parameter):
class Derived : public Base<int>
{
public:
typedef int base_param_type;
}
More involved solution: What you can do, at least for an expected subset of possible Us, is the following:
template <typename DerivedType,
typename BaseParamType = DerivedType,
bool = std::is_base_of<Base<BaseParamType>, DerivedType>::value>
struct Extract
{
typedef BaseParamType type;
};
template <typename T, typename U>
struct Extract<T, U, false>;
int main()
{
Extract<DerivedCRTP>::type; // CRTP - trivial
Extract<Derived, int>::type; // type == int, Derived is derived from Base<int>
Extract<Derived, double>::type; // compile-time error, undefined template
return 0;
}
This isn't as convenient as passing some instance of a type to a deducing template function and have it magically , but you can at least test if some type T derives from Base<U> and get a compile-time error if it doesn't.
Since the base class has to be a concrete class (not a template), it is not possible to know whether it is a template or a non-template class.
In another words :
struct A1 : public B1
{};
struct A2 : public B2<int>
{};
in both of these cases both base classes are concrete types.

Specializing a class template method for derived classes

I'd appreciate help specializing the method doIt() in the following bit of code for classes that share a common base class, as shown below
#include <iostream>
#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
struct BarBase {};
struct Bar: BarBase {};
struct FooBase {};
struct Foo: FooBase {};
template <typename T>
struct Task
{
// I'd like to specialize this method for classes with a common base class
void doIt();
};
// my attempt (does not compile)
template <typename T>
typename boost::enable_if<boost::is_base_of<FooBase, T> >::value
doIt() {
std::cout << "Type is derived from FooBase\n";
}
int main()
{
Task<Foo> f;
f.doIt();
}
You can't specialise a template class member. You can specialise a class, and every specialisation is a complete separate class that inherits nothing from the unspecialised template (it may or may not have all or some of the members of the unspecialised class).
What you can also do is have a template member function in a template class, and specialise that. So you can do this:
template <typename T>
struct Task
{
void doIt() { doItPriv<T>(); }
private:
template<typename T1>
void doItPriv();
};
and then specialise doItPriv.
According to this answer,
SFINAE only works if substitution in argument deduction of a template
argument makes the construct ill-formed.
That is why you cannot do smth like that:
template <typename T>
struct Task
{
typename std::enable_if<std::is_base_of<FooBase, T>::value>::type doIt() {
std::cout << "Type is derived from FooBase\n";
}
typename std::enable_if<std::is_base_of<FooBase, T>::value == false>::type doIt()
{
}
};
Here doIt() isn't template, so no any deduction.
But you can do the following:
template <typename T1>
struct Task
{
template <typename T>
typename std::enable_if<std::is_base_of<FooBase, T>::value>::type doIt_() {
std::cout << "Type is derived from FooBase\n";
}
template <typename T>
typename std::enable_if<std::is_base_of<FooBase, T>::value == false>::type doIt_()
{
}
void doIt()
{
doIt_<T1>();
}
};