您可以使用可变参数模板和SFINAE,仅启用类型参数满足您(或任意)条件的构造函数。
#include <type_traits>
struct A {};
struct B {};
您需要使用 type_traits
来处理 std::false_type
和 std::true_type
。
alternates
模板是关键。它的目标是仅当 T1
、...Tn
列表交替出现 X
和 Y
时,alternates<X, Y, T1, T2, T3, ..., Tn>
才从 std::true_type
继承。默认选择(如下)为否,但我们会为匹配的情况特化。
template <typename X, typename Y, typename... Ts>
struct alternates : std::false_type {}
我选择将这个模板更加通用,允许alternates<X, Y>
从true_type
继承。空列表满足数学要求,即其中所有元素交替。这将是下面递归定义的一个好的权宜之计。
template <typename X, typename Y>
struct alternates<X, Y> : std::true_type {}
如果且仅当Ts...
中第一个元素不在列表中时,Ts...
减去第一个元素会与Y
和X
(Y
首先!)交替出现。alternates<X, Y, Ts...>
的任何其他列表也是如此。
template <typename X, typename Y, typename... Ts>
struct alternates<X, Y, X, Ts...>
: alternates<Y, X, Ts...> {};
struct C
{
我们将构造函数定义为一个模板,它首先接受一个参数包(类型将被推导,调用时不需要指定),并且具有用于SFINAE目的的默认模板参数。如果无法基于参数包计算出默认参数,则构造函数将不存在。我从示例中添加了关于键值对数量的额外条件,这些条件是我假设的。
template<typename... Ts,
typename = typename std::enable_if<
sizeof...(Ts) % 2 == 1 &&
sizeof...(Ts) >= 3 &&
alternates<A, B, Ts...>::value
>::type>
C(Ts&&...);
};
SFINAE的工作方式是,只有当
condition
为真时,
std::enable_if<condition, T>::type
(即
::type
部分)才会被定义。这可以是任意可在编译时计算的布尔表达式。如果它为假,则在结尾处使用
::type
将导致
替换失败,并且您尝试使用它的重载(例如
C{A(), A(), A()}
)将不会被定义。
您可以测试下面的示例是否按预期工作。注释掉的那些不应该工作。
int main() {
C c1 { A(), B(), A() };
C c2 { A(), B(), A(), B(), A(), B(), A() };
}
Try the code here.
std::pair<A, B>&&
。 - user10605163static_assert
或SFINAE确保模板参数符合预期。 - user10605163