C ++ Несколько шаблонных пакетов параметров по специализации - PullRequest
0 голосов
/ 26 июня 2018

Я играю с пакетами параметров и получил поведение, которое не смог объяснить (в моем понимании оно должно быть действительным).

У меня есть шаблонная структура, принимающая один тип и специализацию с использованием двух пакетов параметров (http://coliru.stacked -crooked.com / a / 0cb6c6fef7b09e6f ):

    #include <type_traits>
    template<class, int> struct dummy{};
    template<class...> struct multiple{};

    template<class T, class = std::true_type>
    struct exactly_one_not_0 : std::false_type {};

    template<class...LTs, class...RTs, class T, int V>
    struct exactly_one_not_0
    <
        multiple
        <
            dummy<LTs, 0>...,
            dummy<T, V>,
            dummy<RTs, 0>...
        >,
        std::bool_constant<V>
    > : std::true_type
    {};

    template<class T>
    constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;

    using d0 = dummy<int,0>;
    using d1 = dummy<int,1>;

    static_assert(exactly_one_not_0_v<multiple<d1>>);
    static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
    static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
    static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);

    static_assert(!exactly_one_not_0_v<multiple<>>);
    static_assert(!exactly_one_not_0_v<multiple<d0>>);
    static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
    static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
    static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
    static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
    static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);

Первые четыре утверждения терпят неудачу. Зачем? Специализация должна уметь ловить эти случаи.

Для сравнения следующий код работает должным образом (http://coliru.stacked -crooked.com / a / 871ee1cc28f5ddc9 ):

    #include <iostream>
    template<class T>
    struct multi_pack
    {
          static void f() {std::cout << "base case\n";}
    };

    template<class...Ts1, class...Ts2>
    struct multi_pack<std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>>
    {
        using T = std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>;
        static void f() {std::cout << "special case\n";}
    };

Есть идеи, почему первый пример не работает, а второй работает?

1 Ответ

0 голосов
/ 26 июня 2018

Во втором случае вы просите, чтобы компилятор соответствовал пакетам внутри отдельных типов (есть пара кортежей Ts1 и кортеж Ts2). Это определенно однозначно.

В первом примере оба пакета используются в одном пакете параметров, разделенных третьим типом. Я понимаю, почему вы ожидаете, что это сработает, но, очевидно, компилятор (и gcc, и clang) отказывается полностью соответствовать пакетам с префиксом.

В вашем конкретном случае это на самом деле неоднозначно в нескольких случаях: поскольку V является параметром, он также может быть равен 0, что делает его неоднозначным для соответствия последовательности d0. Во всяком случае, я пытался, что даже использование постоянной 1 не решает проблему. Вам придется удалить префикс самостоятельно.

#include <type_traits>
template<class, int> struct dummy{};
template<class...> struct multiple{};

template<class T, class = void> // <-- was this supposed to be an enabler?
struct exactly_one_not_0 : std::false_type {};

template<class Ts, class...RTs, int V>
struct exactly_one_not_0
<
    multiple
    <
        dummy<Ts, V>,
        dummy<RTs, 0>...
    >,
    std::enable_if_t<V!=0>
> : std::bool_constant<V>
{};

template<class LT, class...RDummies>
struct exactly_one_not_0
<
    multiple
    <
        dummy<LT, 0>,
        RDummies...
    >
> : exactly_one_not_0<multiple<RDummies...>>
{};

template<class T>
constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;

using d0 = dummy<int,0>;
using d1 = dummy<int,1>;

static_assert(exactly_one_not_0_v<multiple<d1>>);
static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);

static_assert(!exactly_one_not_0_v<multiple<>>);
static_assert(!exactly_one_not_0_v<multiple<d0>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);

int main()
{
    return 0;
}

Ради того, чтобы не создавать несколько multiple s, вы можете также удалить это

template<class T>
struct exactly_one_not_0 : std::false_type {};

template<class Enable, class... Ts> // <-- was this supposed to be an enabler?
struct exactly_one_not_0_impl : std::false_type {};

template<class... Ts>
struct exactly_one_not_0<multiple<Ts...>>
    : exactly_one_not_0_impl<void, Ts...>
{};

template<class Ts, class...RTs, int V>
struct exactly_one_not_0_impl
<
    std::enable_if_t<V!=0>,
    dummy<Ts, V>,
    dummy<RTs, 0>...
> : std::bool_constant<V>
{};

template<class LT, class...RDummies>
struct exactly_one_not_0_impl
<
    void,
    dummy<LT, 0>,
    RDummies...
> : exactly_one_not_0_impl<void, RDummies...>
{};
...