是否可以将“B<get_item<0,Ts...>>”更改为“template_with_params<B, 1, ...Ts>”?

Is it possible to change `B<get_item<0,Ts...>>` to `template_with_params<B, 1, ...Ts>`?

我正在开发一个 SFINAE 程序,如果参数 class 或模板 class 不是集合的基础,则从程序中删除函数模板实例(参见 How to get a SFINAE expression to work with template and non-template classes?) :

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Classes to test on
class A{};
template <typename T> class B{};
template <typename T0, typename T1> class BB{};
class C{};
template <typename T> class D{};
template <typename T0, typename T1> class DD{};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Collection of valid types
template <
    template <typename...> class TT
    , typename...Ts>
class collection 
    : A                                        // class A
    , B<get_item<0,Ts...>>                     // class B<X>
    , BB<get_item<0,Ts...>, get_item<1,Ts...>> // class BB<Y, Z>
{};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Function to enable on
template <typename T>
enable_if_is_base_of<T, collection> test(T&&)
{    return enable_if_is_base_of<T, collection>();
}

现在我想知道 B<X>BB<Y,Z> 的规范是否可以以某种方式从 BB<get_item<0,Ts...>, get_item<1,Ts...>> 反转为 template_with_params<BB, X, 2, ...Ts> 其中 returns 是 BB<T0, T1> 的类型,其中 T0Ts... 的第一个模板参数,而 T1 是 [= 的第二个模板参数21=]。此外,如果 Ts 中没有足够的元素,则将使用默认的 X 来处理其余部分。可能吗?

#include <cstddef>
#include <utility>
#include <type_traits>
#include <tuple>

template <std::size_t... Is>
struct index_sequence {};

template <std::size_t N, std::size_t... Is>
struct make_index_sequence_h : make_index_sequence_h<N - 1, N - 1, Is...> {};

template <std::size_t... Is>
struct make_index_sequence_h<0, Is...> { using type = index_sequence<Is...>; };

template <std::size_t N>
using make_index_sequence = typename make_index_sequence_h<N>::type;

template <template <typename...> class T, typename D, std::size_t N, typename... Ts>
struct template_with_params_impl;

template <template <typename...> class T, typename D, std::size_t N, std::size_t... Is, std::size_t... Js, typename... Ts>
struct template_with_params_impl<T, D, N, index_sequence<Is...>, index_sequence<Js...>, Ts...>
{
    using type = T<typename std::tuple_element<Is, std::tuple<Ts...>>::type..., typename std::remove_reference<decltype(void(Js), std::declval<D>())>::type...>;
};

template <template <typename...> class T, typename D, std::size_t N, typename... Ts>
using template_with_params = typename template_with_params_impl<T, D, N, make_index_sequence<sizeof...(Ts) >= N ? N : sizeof...(Ts)>, make_index_sequence<sizeof...(Ts) >= N ? 0 : N - sizeof...(Ts)>, Ts...>::type;

// if you want `void' to be the hardcoded default parameter, use below alias instead:
// template <template <typename...> class T, std::size_t N, typename... Ts>
// using template_with_params = typename template_with_params_impl<T, void, N, make_index_sequence<sizeof...(Ts) >= N ? N : sizeof...(Ts)>, make_index_sequence<sizeof...(Ts) >= N ? 0 : N - sizeof...(Ts)>, Ts...>::type;

测试:

#include <iostream>

template <typename... Ts>
struct B { void foo() { std::cout << 1; } };

int main()
{    
    template_with_params<B, void, 3, int> b{}; b.foo(); // 1

    static_assert(std::is_same< template_with_params<B, void, 3, int>, 
                                B<int, void, void> >{}, "!");

    static_assert(std::is_same< template_with_params<B, void, 1, int, float>, 
                                B<int> >{}, "!");
}

DEMO

可以用递归来实现,一个接一个地应用参数:

// Apply a single type parameter to a template, producing a new template
template<template<typename...> class TT, typename T>
struct apply_partial {
   template<typename... Ts>
   using type = TT<T, Ts...>;
};

// Apply parameters from I upto N, one after another, using apply_partial
template<template<typename...> class TT, std::size_t I, std::size_t N, typename... Ts>
struct apply_seq {
   typedef typename apply_seq<
         apply_partial<TT, get_item<I, Ts...>>::template type,
         I+1, N, Ts...
      >::type type;
};

template<template<typename...> class TT, std::size_t N, typename... Ts>
struct apply_seq<TT, N, N, Ts...> {
   typedef TT<> type;
};

// Main template
template<template<typename...> class TT, std::size_t N, typename... Ts>
using template_with_params = typename apply_seq<TT, 0, N, Ts...>::type;

它可以编译,但我还没有广泛测试它。还可以通过 apply_seq.

的其他模板参数和特化来添加默认值等其他功能

对于 void 默认值,您需要添加这种情况:

template<template<typename...> class TT, std::size_t I, std::size_t N>
struct apply_seq<TT, I, N> {
   typedef typename apply_seq<TT, I, N, void>::type type;
};