Specialize class for Base and all Derived - c++

I have a Base-class and a derived class. I would like to design a Class C that reacts differently when a base or derived object is handed over like this:
template<typename T>
class Base {};
class Derived: public Base<int>{}
template<typename T>
class C
{
public:
static int f(T&& arg) {return 1;};
};
// Specialize C, not the function
template<typename T>
class C<T>
{
public:
static int f(T&& arg) {return 2;};
};
template<typename T>
int function(T&& arg) {
return C<T>::f(std::forward<T>(arg));
}
int main()
{
std::cout << function(1) << std::endl;
std::cout << function(Base<float>()) << std::endl;
std::cout << function(Derived()) << std::endl;
}
This is supposed to print:
1
2
2
Is there a way that I can do this with specializing the class C and not the function. In reality I just hand over the type C and everything happens out of my reach.
To clearify, the implementation of function may look like that but I can't really tell. All I know is the signature of f: int f(T&&); And that I have to have a class C that has a function of that signature.

Through Specialization of function():
Example code first:
template< class T >
struct Base
{};
struct Child : Base<int>
{ };
template<typename T>
class C1
{
public:
static int f(T&& arg) {return 1;}
};
template<typename T>
class C2
{
public:
static int f(T&& arg) {return 2;}
};
template<typename T>
typename std::enable_if< std::is_base_of<Base<int>, T>::value, int >::type function(T&& arg)
{
return C2<T>::f(std::forward<T>(arg));
}
template<typename T>
typename std::enable_if< !std::is_base_of<Base<int>, T >::value, int >::type function(T&& arg)
{
return C1<T>::f(std::forward<T>(arg));
}
int main()
{
std::cout << function(1) << std::endl;
std::cout << function(Base<int>()) << std::endl;
std::cout << function(Child()) << std::endl;
return 0;
}
Why:
So the split here is that I've written two versions of function(). The return type of function() looks a little hairy but the first version will be compiled if T has Base< int > as a base type. Otherwise the second version will be used.
std::enable_if<> is a neat tool which causes the template expansion to fail if its condition is not met. In this case we cause the template expansion to fail if T does not have Base< int > as a base class. In the case that the template expansion fails the compiler continues to look for another version of function() which will compile.
C1 and C2 are then two different objects with C2 being used if T has Base as a child, otherwise C1 is used.
Through Specialization of C:
template< class BaseArgument >
Base< BaseArgument > get_Base_type_of( Base< BaseArgument >&& );
template< class T >
struct has_Base
{
typedef char yes;
typedef long no;
template< typename T1 >
static yes Check( decltype( get_Base_type_of( declval<T1>() ) )* );
template< typename T1 >
static no Check( ... );
static const bool value = sizeof( decltype( Check<T>(0) ) ) == sizeof( yes ) ;
};
template< typename T, typename Placeholder = void >
class C
{
public:
static int f(T&& arg) {return 1;};
};
template<typename T>
class C< T, typename std::enable_if< has_Base<T>::value >::type >
{
public:
static int f(T&& arg) {return 2;};
};

Related

Casting an object to a subset of of its bases

I have a class defined as follows:
template< typename... >
class Base;
template< typename T >
class Base<T>{
//pure virtual functions dependent on T
public:
virtual ~Base() = default;
};
template< typename... Ts >
class Base<Ts...>
: public Base<Ts>...
{
//empty
};
The questions are:
Is there a way to cast Base< T1, T2, T3 >& to Base< T2, T3 >&?
Is there a way to cast Base< T1, T2, T3 >& to Base< T2, T1, T3 >&?
EDIT:
Let me rephrase the question without the templates.
I have a class defined as follows:
class Base_int{
public:
virtual int f() = 0;
virtual ~Base_int() = default;
};
class Base_float{
public:
virtual float f() = 0;
virtual ~Base_float() = default;
};
class Base_double{
public:
virtual double f() = 0;
virtual ~Base_double() = default;
};
class IFD
: public Base_int
, public Base_float
, public Base_double
{ };
class IF
: public Base_int
, public Base_float
{ };
class FID
: public Base_float
, public Base_int
, public Base_double
{ };
The questions are:
Is there a way to cast IFD& to IF&?
Is there a way to cast IFD& to FID&?
EDIT 2:
To further clarify:
I'm impementing a generic visitor pattern implementation which I will be using in a generic tree container. I'm using the mixin (CRTP) pattern to implement accept method of visited classes as well as visit method of visitors. These methods are virtual and I need the hierarchy I posted in the OP.
There is a base class called IVisitor<Ts...> that implements an interface of the visitor that visits Ts.... This interface is composed of classes IVisitor<T> - just as in the OP.
A concrete visitor that implements the IVisitor<Ts...> is defined as follows:
class ConcreteVisitor : public IVisitor<T1, T2, T2, ...>{
//Implementation of the visit methods defined in IVisitor<Ts...>
virtual void visit( T1& v ) override { /*...*/ }
virtual void visit( T2& v ) override { /*...*/ }
virtual void visit( T3& v ) override { /*...*/ }
}
It occurred to me that it would be useful if the ConcreteVisitor could visit stuff in the IVisitor<Us...> where Us... is a subset, or a permutation of Ts.... In order to do that, I need to cast it to that base - hence the question.
After examining the problem some more I realized that what I needed a wrapper that adapts generic visitors to my interfaces. This is the solution I went with - see the IVisitable::adaptVisitor static method:
#include <iostream>
#include <typeinfo>
#include <vector>
//Visitable base class
template< typename >
class Visitable;
//Visitable class interface
template< typename... >
class IVisitor;
//Assures that the visitor type derives from IVisitor
template< typename IVisitor_type >
class IVisitable
: public IVisitable<typename IVisitor_type::derives_from_IVisitor>
{ };
//Implements the Visitor adapter
template< typename Derived, typename Root, typename... Ts >
class VisitorWrapper;
//IVisitable base class
template< typename... Ts >
class IVisitable< IVisitor<Ts...> >{
public:
//Visitor interface type
using IVisitor = IVisitor<Ts...>;
//Visitor adapter type
template< typename F >
class VisitorAdapter;
//Helper function
template< typename F >
static VisitorAdapter<F&&> adaptVisitor( F&& f )
{ return { std::forward<F>(f) }; }
//Accept visitor pure virtual methods
virtual void accept( IVisitor& ) = 0;
virtual void accept( IVisitor& ) const = 0;
virtual void accept( IVisitor&& ) = 0;
virtual void accept( IVisitor&& ) const = 0;
protected:
virtual ~IVisitable() = default;
};
//Implements the visitor adapter of F class
template< typename... Ts >
template< typename F >
class IVisitable<IVisitor<Ts...>>::VisitorAdapter
: public VisitorWrapper< VisitorAdapter<F>, ::IVisitor<Ts...>, Ts... >
//Derives from VisitorWrapper that implements all of the virtual methods
{
public:
template< typename U >
void visit( U&& u ){ f( std::forward<U>(u) ); }
VisitorAdapter( F f_ ) : f(f_) { }
F f;
};
//Basic IVisitor of T
template< typename T >
class IVisitor<T>{
public:
using derives_from_IVisitor = IVisitor;
virtual void visit( T& ) = 0;
virtual void visit( const T& ) = 0;
virtual ~IVisitor() = default;
};
//IVisitor of Ts derives from Visitor<T>
template< typename T, typename... Ts >
struct IVisitor<T, Ts...>
: IVisitor<T>, IVisitor<Ts...>
{
using derives_from_IVisitor = IVisitor;
};
//Visitable base class. Final to prevent errors
template< typename Derived >
struct Visitable final
{
//Extends class wraps the Base class inheritance
template< typename Base >
class extends : public Base
{
public:
//Propagate the IVisitor interface.
using IVisitor = typename Base::IVisitor;
//Prevents incomprehensible errors when visitor doesn't
//define Derived as its visited class
static_assert(
std::is_base_of<::IVisitor<Derived>, IVisitor>::value
, "Base class visitor interface does not support visitation of this type"
);
//Implement accept method via CRTP
virtual void accept( IVisitor& v ) override{
static_cast< ::IVisitor<Derived>& >(v).visit( // Disambiguation
static_cast<Derived&>(*this) // CRTP
);
}
virtual void accept( IVisitor& v ) const override {
static_cast< ::IVisitor<Derived>& >(v).visit( // Disambiguation
static_cast<const Derived&>(*this) // CRTP
);
}
virtual void accept( IVisitor&& v ) override{
static_cast< ::IVisitor<Derived>&& >(v).visit( // Disambiguation
static_cast<Derived&>(*this) // CRTP
);
}
virtual void accept( IVisitor&& v ) const override{
static_cast< ::IVisitor<Derived>&& >(v).visit( // Disambiguation
static_cast<const Derived&>(*this) // CRTP
);
}
protected:
virtual ~extends() = default;
};
~Visitable() = delete;
};
template< typename > struct print_type;
//Uses CRTP to implement visit method of IVisitor
//Consumes the list of Ts... so that the inheritance hierarchy is linear
template<
typename Derived // CRTP
, typename... Rs // Base class (IVisitor<Rs...>)
, typename T // Currently implemented type
, typename... Ts // The rest of types
> class VisitorWrapper< Derived, IVisitor<Rs...>, T, Ts... >
: public VisitorWrapper< Derived, IVisitor<Rs...>, Ts... > //Consume T
{
//Cast to Derived and call visit method
virtual void visit( T& v ){ static_cast<Derived*>(this)->visit(v); }
virtual void visit( const T& v ){ static_cast<Derived*>(this)->visit(v); }
};
//Uses CRTP to implement visit method of IVisitor
template< typename Derived, typename... Rs, typename T >
class VisitorWrapper< Derived, IVisitor<Rs...>, T >
: public IVisitor<Rs...> //IVisitor<Ts...>
{
virtual void visit( T& v ){ static_cast<Derived*>(this)->visit(v); }
virtual void visit( const T& v ){ static_cast<Derived*>(this)->visit(v); }
};
/////////////////////////////////////////////////////
class ABCVisitor
: public IVisitor< class A, class B, class C >
{ };
class ABC : public IVisitable< ABCVisitor >
{};
class A : public Visitable<A>::extends<ABC> {};
class B : public Visitable<B>::extends<ABC> {};
class C : public Visitable<C>::extends<B> {};
int main()
{
auto p = [](const auto& v){ std::cout << typeid(v).name() << std::endl; };
auto printer = ABC::adaptVisitor( p );
A a; B b; C c;
std::vector< ABC* > vs{ &a, &b, &c };
for( const auto& v : vs )
v->accept(printer);
return 0;
}
I am not really sure if this is what you are looking for, but here it goes.
I would make the accept method of the base classes templated so that they can accept any visitor as long as the visitor has the right concept. In particular:
#include <iostream>
template <class Derived>
struct Base {
// templated visitor so that it can take any argument as long as it has the right concept
template <class Visitor>
void accept(Visitor& visitor) {
visitor.visit(static_cast<Derived&>(*this));
}
};
struct T1
: Base<T1> {};
struct T2
: Base<T2> {};
struct T3
: Base<T3> {};
struct Visitor1 {
void visit(T1& t1) {
std::cout << "Visiting T1 from Visitor1" << std::endl;
}
void visit(T2& t2) {
std::cout << "Visiting T2 from Visitor1" << std::endl;
}
};
struct Visitor2 {
void visit(T1& t1) {
std::cout << "Visiting T1 from Visitor2" << std::endl;
}
void visit(T2& t2) {
std::cout << "Visiting T2 from Visitor2" << std::endl;
}
void visit(T3& t3) {
std::cout << "Visiting T3 from Visitor2" << std::endl;
}
};
int main(int argc, const char * argv[]) {
Visitor1 visitor1;
Visitor2 visitor2;
T1 t1;
T2 t2;
T3 t3;
t1.accept(visitor1);
t1.accept(visitor2);
t2.accept(visitor1);
t2.accept(visitor2);
t3.accept(visitor2);
// t3.accept(visitor1); // triggers a compilation error as visitor1 cannot handle T3 types
}
This way you no longer have to do any casts of your visitor classes.
I don't think you need to cast to visitor with permuted parameters to accomplish what you want, the only thing you need is additional level of abstraction - variadic template wrapper to your visitor. Find the code below:
struct VisitorParent {
virtual ~VisitorParent() = default;
};
template <class T>
struct VisitorImpl: virtual VisitorParent {
virtual void visit(T &t) {
//...
}
};
template <class... Ts>
struct Visitor: virtual VisitorImpl<Ts>... { };
template <class T>
struct VisitorWrapperImpl {
VisitorParent *vp;
VisitorWrapperImpl(VisitorParent *vp): vp(vp) {}
void visit(T &t) {
VisitorImpl<T> *cvi = dynamic_cast<VisitorImpl<T> *>(vp);
if (cvi) {
cvi->visit(t);
}
}
};
template <class... Ts>
struct VisitorWrapper: VisitorWrapperImpl<Ts>... {
VisitorWrapper(VisitorParent *vp): VisitorWrapperImpl<Ts>(vp)... { }
template <class T>
void visit(T &t) {
VisitorWrapperImpl<T>::visit(t);
}
};
int main() {
Visitor<int, char, float> v;
VisitorWrapper<char,float> vw(&v);
char c;
vw.visit(c);
}
Another idea would be to have the accept method taking one of the base classes of the visitor. This way you can pass any visitor to the accept method since all visitors can be casted to the single base visitor class. I believe this will work for you:
#include <iostream>
// one of the base classes of your concrete visitors
template <class T>
struct VisitorUnit {
virtual void visit(T& t) = 0;
};
// the visitor interface which will be implemented by concrete visitors
template <class... T>
struct Visitor
: VisitorUnit<T>... {
};
// the base class of your leaf classes
template <class Derived>
struct Base {
void accept(VisitorUnit<Derived>& visitor) {
visitor.visit(static_cast<Derived&>(*this));
}
};
struct T1: Base<T1>{};
struct T2: Base<T2>{};
struct T3: Base<T3>{};
struct Visitor1
: Visitor<T1, T2, T3> {
void visit(T1& t) override {
std::cout << "T1 from Visitor1" << std::endl;
}
void visit(T2& t) override {
std::cout << "T2 from Visitor1" << std::endl;
}
void visit(T3& t) override {
std::cout << "T3 from Visitor1" << std::endl;
}
};
struct Visitor2
: Visitor<T3, T2> {
void visit(T2& t) override {
std::cout << "T2 from Visitor2" << std::endl;
}
void visit(T3& t) override {
std::cout << "T3 from Visitor2" << std::endl;
}
};
int main(int argc, const char * argv[]) {
T1 t1;
T2 t2;
T3 t3;
Visitor1 visitor1;
Visitor2 visitor2;
visitor1.visit(t1);
visitor1.visit(t2);
visitor1.visit(t3);
visitor2.visit(t2);
visitor2.visit(t3);
}

boost::variant and polymorphism

I want to get pointer to base class from boost variant, if I put orignally pointer to derived class. Is there some way to achive this . The following code does not work.
class A{ public: virtual ~A(){}}; class B : public A{};
typedef boost::variant<A*,B*> MyVar;
MyVar var = new B;
A* a = boost::get<A*> (var); // the following line throws exception
Maybe someone have idea how to write my own get function which will test if the requested type is base class of the stored type of in the variant,and then do the appropriate cast
You can write your own visitor with templated operator() like below:
LIVE DEMO
#include <iostream>
#include <boost/variant.hpp>
#include <type_traits>
struct A { virtual ~A() {} virtual void foo() {} };
struct B : A { virtual void foo() { std::cout << "B::foo()" << std::endl; } };
template <typename T>
struct visitor : boost::static_visitor<T>
{
private:
using Base = typename std::remove_pointer<
typename std::remove_cv<
typename std::remove_reference<T>::type
>::type
>::type;
template <typename U>
T get(U& u, std::true_type) const
{
return u;
}
template <typename U>
T get(U& u, std::false_type) const
{
throw boost::bad_get{};
}
public:
template <typename U>
T operator()(U& u) const
{
using Derived = typename std::remove_pointer<
typename std::remove_cv<
typename std::remove_reference<U>::type
>::type
>::type;
using tag = std::integral_constant<bool
, (std::is_base_of<Base, Derived>::value
|| std::is_same<Base, Derived>::value)
&& std::is_convertible<U, T>::value>;
return get(u, tag{});
}
};
template <typename T, typename... Args>
T my_get(boost::variant<Args...>& var)
{
return boost::apply_visitor(visitor<T>{}, var);
}
int main()
{
boost::variant<A*,B*> var = new B;
A* a = my_get<A*>(var); // works!
a->foo();
B* b = my_get<B*>(var); // works!
b->foo();
}
Output:
B::foo()
B::foo()
Q & A section:
This solution is weird!
No, it is not. This is exactly what the visitor classes in Boost.Variant are for. Similar solution already exists in latest release of Boost.Variant, which is boost::polymorphic_get<T>. Sadly it was designed for other purposes and cannot be used here.
Hi thank you all for your answers and comments
I came to the following which decides at compile time if types are inherited from each other. And it seems to work, and it seems much easier to me to understand.
#include <iostream>
#include <boost/variant.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility.hpp>
using namespace boost::type_traits;
struct A { virtual ~A() {} virtual void foo() {} };
struct B : A { virtual void foo() { std::cout << "B::foo()" << std::endl; } };
typedef boost::variant<B*,A*,C*> MyVar;
template <typename A,typename B>
struct types_are_inheritance_related
{
static const bool value=
ice_or<
boost::is_base_of<A, B>::value,
boost::is_base_of<B, A>::value
>::value;
};
template<class Base>
class get_visitor
: public boost::static_visitor<Base*> { public:
template<class T>
Base* operator()( T* t, typename boost::enable_if<types_are_inheritance_related<Base,T> >::type* dummy = 0)
{
Base* b = dynamic_cast<Base*> ( t);
return b;
}
template<class T>
Base* operator()( T* t, typename boost::disable_if<types_are_inheritance_related<Base,T> >::type* dummy = 0)
{
return 0;
}
};
template<class T>
T* get_var_value(MyVar& var)
{
get_visitor<T> visitor;
T* aa= var.apply_visitor(visitor);
return aa;
}
int main()
{
MyVar var = new B;
A* a = get_var_value<A*>(var); // works!
a->foo();
B* b = get_var_value<B*>(var); // works!
b->foo();
}

Overload template member function over any container of A or B

I have 2 existing classes A and B. I want to implement a third struct C such that C implement operator() which would take any container of A or using a different implementation any container of B. Is it possible to do a such thing using template specialization ?
enable_if is very useful in lots of scenarios, but in cases like these I'm usually more inclined to use tag dispatching. To my eye, the code looks cleaner, behaves more predictably, and if you try to use it wrongly, the error messages make slightly more sense.
struct C
{
template <class T>
void operator()(const T& container) const
{
operator()(container, Tag<typename T::value_type>());
}
private:
template <class V> struct Tag {};
template <class T>
void operator()(const T& container, Tag<A>) const
{
std::cout << "A\n";
}
template <class T>
void operator()(const T& container, Tag<B>) const
{
std::cout << "B\n";
}
};
int main()
{
std::vector<A> a;
std::list<B> b;
C c;
c(a);
c(b);
}
You can do this using template template parameters.
Assuming your containers have two parameters (content type and allocator):
class A {};
class B {};
struct C {
template <
template <
typename,
typename
>
class V
>
void operator()(const V<A, std::allocator<A> >& /* ca */) const {
}
template <
template <
typename,
typename
>
class V
>
void operator()(const V<B, std::allocator<B> >& /* cb */) const {
}
};
Which would then allow you to do the following:
int main() {
std::vector<A> va;
std::vector<B> vb;
std::list<A> la;
std::list<B> lb;
C c;
c(va);
c(la);
c(vb);
c(lb);
}
Overloads using std::enable_if and std::is_same seem to work. Not very pretty though. Note that these are c++11 features.
struct A {};
struct B {};
struct C {
template<class Cont>
void operator()(const Cont& c, typename std::enable_if<std::is_same<typename Cont::value_type, A>::value>::type * = NULL) const {
std::cout << "a";
}
template<class Cont>
void operator()(const Cont& c, typename std::enable_if<std::is_same<typename Cont::value_type, B>::value>::type * = NULL) const {
std::cout << "b";
}
};
int main() {
std::vector<A> a;
std::list<B> b;
C c;
c(a);
c(b);
}
Yes it should be possible. Arkanosis has a good solution but here is if you want to use template specialization:
class A {};
class B {};
template <template <typename , typename > class Z, class Elem> struct C
{
void operator()(const Z<Elem, std::allocator<Elem>>& v) { std::cout << "General implement" << std::endl; }
};
template <template <typename, typename > class Z> struct C<Z,B>
{
void operator()(const Z<B, std::allocator<B>>& v) { std::cout << "Specialized implement" << std::endl; }
};
Then some client code:
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char** argv)
{
std::vector<A> as;
std::list<B> bs;
C<vector,A> ca;
ca(as);
C<list,B> cb;
cb(bs);
}
If you want to handle different container types from the same C-style class you can derive from templates of the C struct e.g. class MultiContainerHandlerForA : public C<vector,A>, public C<list,A>.

c++ template specialization for all subclasses

I need to create a template function like this:
template<typename T>
void foo(T a)
{
if (T is a subclass of class Bar)
do this
else
do something else
}
I can also imagine doing it using template specialization ... but I have never seen a template specialization for all subclasses of a superclass. I don't want to repeat specialization code for each subclass
You can do what you want but not how you are trying to do it! You can use std::enable_if together with std::is_base_of:
#include <iostream>
#include <utility>
#include <type_traits>
struct Bar { virtual ~Bar() {} };
struct Foo: Bar {};
struct Faz {};
template <typename T>
typename std::enable_if<std::is_base_of<Bar, T>::value>::type
foo(char const* type, T) {
std::cout << type << " is derived from Bar\n";
}
template <typename T>
typename std::enable_if<!std::is_base_of<Bar, T>::value>::type
foo(char const* type, T) {
std::cout << type << " is NOT derived from Bar\n";
}
int main()
{
foo("Foo", Foo());
foo("Faz", Faz());
}
Since this stuff gets more wide-spread, people have discussed having some sort of static if but so far it hasn't come into existance.
Both std::enable_if and std::is_base_of (declared in <type_traits>) are new in C++2011. If you need to compile with a C++2003 compiler you can either use their implementation from Boost (you need to change the namespace to boost and include "boost/utility.hpp" and "boost/enable_if.hpp" instead of the respective standard headers). Alternatively, if you can't use Boost, both of these class template can be implemented quite easily.
I would use std::is_base_of along with local class as :
#include <type_traits> //you must include this: C++11 solution!
template<typename T>
void foo(T a)
{
struct local
{
static void do_work(T & a, std::true_type const &)
{
//T is derived from Bar
}
static void do_work(T & a, std::false_type const &)
{
//T is not derived from Bar
}
};
local::do_work(a, std::is_base_of<Bar,T>());
}
Please note that std::is_base_of derives from std::integral_constant, so an object of former type can implicitly be converted into an object of latter type, which means std::is_base_of<Bar,T>() will convert into std::true_type or std::false_type depending upon the value of T. Also note that std::true_type and std::false_type are nothing but just typedefs, defined as:
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
I know this question has been answered but nobody mentioned that std::enable_if can be used as a second template parameter like this:
#include <type_traits>
class A {};
class B: public A {};
template<class T, typename std::enable_if<std::is_base_of<A, T>::value, int>::type = 0>
int foo(T t)
{
return 1;
}
I like this clear style:
void foo_detail(T a, const std::true_type&)
{
//do sub-class thing
}
void foo_detail(T a, const std::false_type&)
{
//do else
}
void foo(T a)
{
foo_detail(a, std::is_base_of<Bar, T>::value);
}
The problem is that indeed you cannot do something like this in C++17:
template<T>
struct convert_t {
static auto convert(T t) { /* err: no specialization */ }
}
template<T>
struct convert_t<T> {
// T should be subject to the constraint that it's a subclass of X
}
There are, however, two options to have the compiler select the correct method based on the class hierarchy involving tag dispatching and SFINAE.
Let's start with tag dispatching. The key here is that tag chosen is a pointer type. If B inherits from A, an overload with A* is selected for a value of type B*:
#include <iostream>
#include <type_traits>
struct type_to_convert {
type_to_convert(int i) : i(i) {};
type_to_convert(const type_to_convert&) = delete;
type_to_convert(type_to_convert&&) = delete;
int i;
};
struct X {
X(int i) : i(i) {};
X(const X &) = delete;
X(X &&) = delete;
public:
int i;
};
struct Y : X {
Y(int i) : X{i + 1} {}
};
struct A {};
template<typename>
static auto convert(const type_to_convert &t, int *) {
return t.i;
}
template<typename U>
static auto convert(const type_to_convert &t, X *) {
return U{t.i}; // will instantiate either X or a subtype
}
template<typename>
static auto convert(const type_to_convert &t, A *) {
return 42;
}
template<typename T /* requested type, though not necessarily gotten */>
static auto convert(const type_to_convert &t) {
return convert<T>(t, static_cast<T*>(nullptr));
}
int main() {
std::cout << convert<int>(type_to_convert{5}) << std::endl;
std::cout << convert<X>(type_to_convert{6}).i << std::endl;
std::cout << convert<Y>(type_to_convert{6}).i << std::endl;
std::cout << convert<A>(type_to_convert{-1}) << std::endl;
return 0;
}
Another option is to use SFINAE with enable_if. The key here is that while the snippet in the beginning of the question is invalid, this specialization isn't:
template<T, typename = void>
struct convert_t {
static auto convert(T t) { /* err: no specialization */ }
}
template<T>
struct convert_t<T, void> {
}
So our specializations can keep a fully generic first parameter as long we make sure only one of them is valid at any given point. For this, we need to fashion mutually exclusive conditions. Example:
template<typename T /* requested type, though not necessarily gotten */,
typename = void>
struct convert_t {
static auto convert(const type_to_convert &t) {
static_assert(!sizeof(T), "no conversion");
}
};
template<>
struct convert_t<int> {
static auto convert(const type_to_convert &t) {
return t.i;
}
};
template<typename T>
struct convert_t<T, std::enable_if_t<std::is_base_of_v<X, T>>> {
static auto convert(const type_to_convert &t) {
return T{t.i}; // will instantiate either X or a subtype
}
};
template<typename T>
struct convert_t<T, std::enable_if_t<std::is_base_of_v<A, T>>> {
static auto convert(const type_to_convert &t) {
return 42; // will instantiate either X or a subtype
}
};
template<typename T>
auto convert(const type_to_convert& t) {
return convert_t<T>::convert(t);
}
Note: the specific example in the text of the question can be solved with constexpr, though:
template<typename T>
void foo(T a) {
if constexpr(std::is_base_of_v<Bar, T>)
// do this
else
// do something else
}
If you are allowed to use C++20 concepts, all this becomes almost trivial:
template<typename T> concept IsChildOfX = std::is_base_of<X, T>::value;
// then...
template<IsChildOfX X>
void somefunc( X& x ) {...}

Recursively unhide base class members

I try to write a class that takes a tuple of functions as its argument and overloads operator() for all argument_types of the function. Right now this looks like this:
template<typename T>
struct holder {
T t;
};
template<typename T, std::size_t i>
struct overload_helper : public holder<T>, public overload_helper<T, i - 1> {
overload_helper(T t) : holder<T>({t}) {};
typedef typename std::tuple_element<i - 1, T>::type inner_type;
typename inner_type::result_type operator()(typename inner_type::argument_type x) {
return std::get<i - 1>(holder<T>::t)(x); }
};
template<typename T>
struct overload_helper<T, 1> {
typedef typename std::tuple_element<0 ,T>::type inner_type;
typename inner_type::result_type operator()(typename inner_type::argument_type x) {
return std::get<0>(holder<T>::t)(x); }
};
template<typename T>
struct overload : public overload_helper<T, std::tuple_size<T>::value>
{
typedef void result_type;
overload(const T& t) : overload_helper<T, std::tuple_size<T>::value>(t) { }
};
int main() {
auto all = make_overload(std::make_tuple(
std::function<void(double)>([](double i) {
std::cout << "double" << std::endl;
}), std::function<void(int)>([](int i) {
std::cout << "int" << std::endl; })));
all(1); //fails
all(1.0);
}
The problem is that the base class is hiding each recursive definition of operator(). Is it possible to recursively unhide all definitions with using or is the only way to have a templated operator() and pick the right overload with boost::mpl?
using overload_helper<T, i - 1>::operator() in each overload_helper should do the job, so long they aren't ambiguous.