Question
I have a series of ~10 template classes A, B, C, D, ...
I want to enable conversions from a class to previous classes in the series
You can achieve this by constraining a templated constructor (which will be used in conversion) using std::enable_if and some template metaprogramming:
template typename BaseTemplate,
typename From,
typename To,
typename Enable = void>
struct may_convert
: public std::false_type {};
template typename BaseTemplate,
typename T>
struct may_convert, BaseTemplate, void>
: public std::true_type {};
template typename BaseTemplate,
typename T,
typename U>
struct may_convert, BaseTemplate,
typename std::enable_if::value>::type>
: public may_convert> {};
may_convert will walk up the templates of the From template parameter until it is equal to To (in which case it inherits from std::true_type, i.e. may_convert<...>::value is true), or until the templates run out (in which case may_convert<...>::value is false).
Now, all that remains is constraining your constructor appropriately:
template
class A {
public:
A() {}
template >::value>::type>
A(const T&) {}
};
This way, the constructor exists only if may_convert<...>::value is true. Otherwise, the conversion will fail.
Here is an example of how may_convert works in your example (converting from D = A>> to B = A):
The constructor only exists if may_convert::value is true
may_convert matches the last specialization (because D = A and B = A, the parameters are deduced as T = C and U = int) and inherits from may_convert
may_convert again matches the last specialization (T = B, U = int) and inherits from may_convert
This time, the two types are equal, so the first specialization matches, and the entire thing inherits from std::true_type, enabling the constructor.
On the other hand, imagine a using E = A that should not convert to B:
The constructor will only be enabled if may_convert::value is true
may_convert matches the last specialization, and inherits from may_convert
Because double is not an A<...>, none of the specializations match, so we fall back to the default case, which inherits from std::false_type.
Therefore, may_convert::value is false, and the conversion fails.