是否可以将`B <get_item <0,ts ...>&gt;`更改为`template_with_params <b,1,=“”... ts =“”>`?</b,> </get_item < 0,TS ...>

时间:2015-01-02 13:45:59

标签: c++ c++11 sfinae

我正在研究SFINAE程序,如果参数类或模板类不是集合的基础,则从程序中删除函数模板实例(参见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>定义为collection基础的规范是否会以某种方式从BB<get_item<0,Ts...>, get_item<1,Ts...>>转换为template_with_params<BB, X, 2, ...Ts> }返回BB<T0, T1>类型,其中T0Ts...中的第一个模板参数,T1是来自Ts...的第二个模板参数。此外,如果Ts中没有足够的元素,则其余部分将使用默认的X。可能的?

1 个答案:

答案 0 :(得分:1)

#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