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.