Вы можете использовать шаблон переменной и SFINAE, чтобы включить только конструктор, в котором параметры типа удовлетворяют вашему (или любому произвольному) условию.
#include <type_traits>
struct A {};
struct B {};
Вам необходимо type_traits
для std::false_type
и std::true_type
.
Шаблон alternates
является ключом.Цель состоит в том, чтобы alternates<X, Y, T1, T2, T3, ..., Tn>
наследовать от std::true_type
тогда и только тогда, когда список T1
, ... Tn
чередуется X
и Y
.По умолчанию (чуть ниже) выбрано нет , но мы специализируемся на соответствующих случаях.
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 {};
Любой другой список alternates<X, Y, Ts...>
чередуется тогда и только тогда, когда Ts...
минус первый элемент чередуется Y
и X
(Y
first!).
template <typename X, typename Y, typename... Ts>
struct alternates<X, Y, X, Ts...>
: alternates<Y, X, Ts...> {};
struct C
{
Мы определяем конструктор как шаблон, который сначала принимает пакет параметров (тип будет выведен, указывать при вызове нет необходимости), и он имеет параметр шаблона по умолчанию дляСФИНАЕ целей.Если аргумент по умолчанию не может быть рассчитан на основе пакета параметров, конструктор не будет существовать.Я добавил дополнительные условия о количестве пар, которые я предполагал из примера.
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&&...);
};
Способ SFINAE работает так: std::enable_if
определяет только std::enable_if<condition, T>::type
(::type
часть), если condition
верно.Это может быть любое произвольное логическое выражение, вычисляемое во время компиляции.Если оно ложно, то, сказав ::type
в конце, вы получите ошибку замены , и перегрузка, в которой вы пытались его использовать (например, C{A(), A(), A()}
), просто не будет определена.
Вы можете проверить, что приведенные ниже примеры работают должным образом.Закомментированные не будут работать.
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
}
Попробуйте код здесь.