Specializing a class template method for derived classes - c++

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>();
}
};

Related

Solving base class ambiguity for nested template parameter functions

I have classes foo_impl, bar_impl deriving from derived classes foo_derived, bar_derived, and a function that takes in a templated template-parameter:
#include <iostream>
template <typename T>
struct foo_base
{
T a;
};
template <typename T>
struct foo_derived: public foo_base<T>
{
};
struct foo_impl: public foo_derived<int>
{
};
template <typename T>
struct bar_base
{
T a;
};
template <typename T>
struct bar_derived : public bar_base<T>
{
};
struct bar_impl : public bar_derived<int>
{
};
template <typename T, template <class> typename Base>
void useBase(const Base<T>& Arg)
{
std::cout << Arg.a << std::endl;
}
int main()
{
foo_impl foo;
bar_impl bar;
useBase(foo); // ‘const Base<T>’ is an ambiguous base class of ‘foo_impl’
useBase(bar); // ‘const Base<T>’ is an ambiguous base class of ‘bar_impl’
}
Is it possible to specify that I want foo_base and bar_base to be the only types that can be passed to useBase(.)?
Edit: I tried introducing a concept in order to constrain the accepted types, but the compiler is still not happy.
template <typename T, template <class> typename Base>
concept is_base = !std::is_same<std::is_same<Base<T>, foo_base<T>>, std::is_same<Base<T>, bar_base<T>>>::value;
template <typename T, template <class> typename Base>
void useBase(const Base<T>& Arg) requires is_base<T, Base>
{
std::cout << Arg.a << std::endl;
}
As state in comment, overloading might be a possibility:
template <typename T, template <class> typename Base>
void useBaseImpl(const Base<T>& Arg)
{
std::cout << Arg.a << std::endl;
}
template <typename T>
void useBase(const foo_base<T>& arg)
{
useBaseImpl(arg);
}
template <typename T>
void useBase(const bar_base<T>& arg)
{
useBaseImpl(arg);
}

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
}

Select class template specialised for base-class of derived instance

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.

Let template partial specializations share member functions with general instantiations

Suppose I have a class template Foo:
template<typename T, typename U>
struct Foo {
~Foo() = default;
// I want to reuse these methods for all instantiations of Foo
void bar() {}
void poi() {}
};
I want to specialize the destructor for any Foo<T, int>, but I want Foo<T, int> to share the other member functions with the general instantiations. But if I try to do:
template<typename T>
Foo<T, int>::~Foo()
{}
outside the class, it doesn't compile, the error being "invalid use of incomplete type struct Foo<T, int>". What does this error mean, and how can I achieve what I'm trying to do?
It is not possible to partially specialise a (non-templated) member function. You need to specialise the whole class.
One way to do what you want is to inherit the common functions.
template<typename T, typename U> struct MemberProvider
{
~MemberProvider() = default;
void bar() {};
void poi() {};
};
template<typename T, typename U>
struct Foo : public MemberProvider<T, U>
{
~Foo() = default;
// Optionally, you can still do this.
// However, note that these HIDE the inherited functions
void bar() {};
void poi() {};
};
template<typename T> struct Foo<T, int> : public MemberProvider<T, int>
{
~Foo() {};
};
Note you will also need to include any members acted on by the common functions in the inherited class.
However, I would suggest the above represents a code smell. If I saw code like the above in a production environment, I'd be contemplating the presence of a design flaw. Bear in mind that, when destructing such a class, the most derived destructor is invoked before base class destructors. MemberProvider is also not a polymorphic base.
You can explicitly specialize member functions of class templates, but you cannot partially specialize them - which is what you're trying to do.
If you need to partially specialize the destructor in a way that keeps the generic version defaulted, you'll have to partially specialize the whole class template:
template <class T, class U>
struct FooCommon {
~FooCommon() = default;
void bar() {}
void poi() {}
};
template <class T, class U>
struct Foo : FooCommon<T, U> { };
template <class T>
struct Foo<T, int> : FooCommon<T, int> {
~Foo() {
// special logic here
}
};
If foo and poi do things that depend on the template parameters, then you could extract your specialised destructor behaviour into a separate class, e.g.,
#include <iostream>
template <typename T, typename U>
struct Foo;
template <typename T, typename U>
struct FooDestructorTraits
{
static void destroy(Foo<T, U> * ptr)
{
std::cout << "default" << std::endl;
}
};
template <typename U>
struct FooDestructorTraits<int, U>
{
static void destroy(Foo<int, U> * ptr)
{
std::cout << "special" << std::endl;
}
};
template <typename T, typename U>
struct Foo
{
~Foo() noexcept { FooDestructorTraits<T, U>::destroy(this); }
void bar() {}
void baz() {}
};
int main() {
Foo<void, void> x;
Foo<int, void> y;
}

specializing template member function to work in a different way for a special template class

I have two classes class A and class B both of them are template classes for a member function in A I want it to act in a special way when the type of A is B
and in a normal way for any other types I don't know how to do this ?
template <class B>
class B
{
private:
T m;
public:
...... any member functions
}
template <class T>
class A
{
private:
T var;
public:
void doSomething();
};
template <class T>
void A<T>::doSomething(){...........//implementation}
template <class T>
void A<B<T>>::doSomething(){................//different implementation}
You can specialize A this way:
template <class T>
class A<B<T>> {
// ...
};
This is an instance of partial template specialization.
If you refuse to specialize the entire class, you can defer the work from A<T>::doSomething() to a function doSomethingForA<T>(A &) that would be partially specialized, and that would possibly be friend of A<T>.
Hope this solves your problem:
#include <iostream>
template <typename T>
struct B {};
template <typename T> struct A;
template <typename T>
void doSomething(T&) { std::cout << "General\n"; }
template <typename T>
void doSomething(A<B<T>>&) { std::cout << "Special\n"; }
template <typename T>
struct A {
void doSomething() {
::doSomething(*this);
}
};
int main()
{
A<int> general;
A<B<int>> special;
general.doSomething();
special.doSomething();
}