Я предлагаю немного другое решение.
Учитывая тривиальную tag
структуру для обертывания универсального типа (чтобы избежать проблем с типами, которые не могут быть использованы по умолчанию в std::tuples
с)
template <typename>
struct tag
{ };
и вспомогательная структура, определяющая 2 типа на основе std::tuple
template <typename...>
struct getTpls;
template <std::size_t ... Is, typename ... Ts>
struct getTpls<std::index_sequence<Is...>, Ts...>
{
using tpl0 = std::tuple<tag<Ts>...>;
using ftpl = std::tuple<std::tuple_element_t<Is, tpl0>...>;
using stpl = std::tuple<std::tuple_element_t<1u+Is, tpl0>...>;
};
Вы можете написать Collection
следующим образом
template <typename ... Ts>
struct Collection
{
static_assert( sizeof...(Ts) > 1u, "more types, please");
using getT = getTpls<std::make_index_sequence<sizeof...(Ts)-1u>, Ts...>;
using ftpl = typename getT::ftpl;
using stpl = typename getT::stpl;
template <typename ... FTs, typename ... STs,
std::enable_if_t<
std::is_same_v<ftpl, std::tuple<tag<FTs>...>>
&& std::is_same_v<stpl, std::tuple<tag<STs>...>>, int> = 0>
Collection (Node<FTs, STs> ...)
{ }
};
Ниже приведен полный пример компиляции
#include <tuple>
#include <type_traits>
template <typename Start, typename End>
class Node
{ };
struct A {};
struct B {};
struct C {};
template <typename>
struct tag
{ };
template <typename...>
struct getTpls;
template <std::size_t ... Is, typename ... Ts>
struct getTpls<std::index_sequence<Is...>, Ts...>
{
using tpl0 = std::tuple<tag<Ts>...>;
using ftpl = std::tuple<std::tuple_element_t<Is, tpl0>...>;
using stpl = std::tuple<std::tuple_element_t<1u+Is, tpl0>...>;
};
template <typename ... Ts>
struct Collection
{
static_assert( sizeof...(Ts) > 1u, "more types, please");
using getT = getTpls<std::make_index_sequence<sizeof...(Ts)-1u>, Ts...>;
using ftpl = typename getT::ftpl;
using stpl = typename getT::stpl;
template <typename ... FTs, typename ... STs,
std::enable_if_t<
std::is_same_v<ftpl, std::tuple<tag<FTs>...>>
&& std::is_same_v<stpl, std::tuple<tag<STs>...>>, int> = 0>
Collection (Node<FTs, STs> ...)
{ }
};
int main ()
{
Collection<A, B, C> c0{Node<A, B>{}, Node<B, C>{}}; // compile
// Collection<A, B, B> c1{Node<A, B>{}, Node<B, C>{}}; // error!
}