You can use a variadic template and SFINAE to enable only the constructor where the type parameters satisfy your (or any arbitrary) condition.
#include <type_traits>
struct A {};
struct B {};
You need type_traits
for std::false_type
and std::true_type
.
The alternates
template is the key. The goal is to make alternates<X, Y, T1, T2, T3, ..., Tn>
inherit from std::true_type
if and only if the T1
, ... Tn
list is alternating X
and Y
. The default choice (just below) is no, but we specialize for matching cases.
template <typename X, typename Y, typename... Ts>
struct alternates : std::false_type {};
I choose to make this template more generic than your requirement here and allow alternates<X, Y>
to inherit from true_type
. The empty list satisfies the mathematical requirement that all elements of it alternate. This will be a good stopgap for the recursive definition below.
template <typename X, typename Y>
struct alternates<X, Y> : std::true_type {};
Any other list of alternates<X, Y, Ts...>
alternates if and only if Ts...
minus the first element alternate Y
and X
(Y
first!).
template <typename X, typename Y, typename... Ts>
struct alternates<X, Y, X, Ts...>
: alternates<Y, X, Ts...> {};
struct C
{
We define the constructor as a template that first takes a parameter pack (the type will be deduced, no need to specify when calling) and it has a defaulted template parameter for SFINAE purposes. If the defaulted argument cannot be calculated based on the parameter pack, the constructor will not exist. I added the extra conditions about the number of pairs which I assumed from the example.
template<typename... Ts,
typename = typename std::enable_if<
sizeof...(Ts) % 2 == 1 &&
sizeof...(Ts) >= 3 && // did you imply this?
alternates<A, B, Ts...>::value
>::type>
C(Ts&&...);
};
The way SFINAE works is that std::enable_if
only defines the std::enable_if<condition, T>::type
(the ::type
part) if condition
is true. That can be any arbitrary boolean expression computable at compile time. If it is false, saying ::type
at the end will be a substitution failure and the overload where you tried to use it (e.g., C{A(), A(), A()}
) will simply not be defined.
You can test that the examples below work as expected. The ones commented out are not expected to work.
int main() {
C c1 { A(), B(), A() };
C c2 { A(), B(), A(), B(), A(), B(), A() };
// C c3 {}; // I assumed you need at least 2
// C c4 { A(), B(), A(), A() }; // A, A doesn't alternate
// C c5 { B(), A(), B() }; // B, A, B not allowed
// C c6 { A(), B(), A(), B() }; // A, B, A, B doesn't pair
}
Try the code here.
std::pair<A, B>&&
s instead. – Headreachtypesafe
? You can make sure that the template parameters are as expected withstatic_assert
or SFINAE if you need other overloaded constructors. – Headreach