std :: enable_if нельзя использовать для отключения этого объявления - PullRequest
4 голосов
/ 04 июня 2019

У меня есть довольно сложный фрагмент кода, который я упростил до этого репродуктора:

#include <type_traits>
#include <tuple>
template<typename ...As>
struct outer {
    template<typename ...Bs>
    struct inner {
        template<bool dummy, typename E = void>
        struct problem;
        using TA = std::tuple<As...>;
        using TB = std::tuple<Bs...>;

        template<bool dummy>
        struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value < std::tuple_size<TB>::value>::type>
        {
            static constexpr auto val() { return 1; } // actually a complex function
        };

        template<bool dummy>
        struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value >= std::tuple_size<TB>::value>::type>
        {
            static constexpr auto val() { return 0; }
        };
    };
};

int main() {
    return outer<int, float>::inner<double>::problem<false>::val();
}

не компилируется (с gcc или clang), говоря:

<source>:13:82: error: failed requirement 'std::tuple_size<std::tuple<int, float> >::value < std::tuple_size<std::tuple<double> >::value'; 'enable_if' cannot be used to disable this declaration

        struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value <std::tuple_size<TB>::value>::type>

                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~

<source>:27:31: note: in instantiation of template class 'outer<int, float>::inner<double>' requested here

    return outer<int, float>::inner<double>::problem<false>::val();

Я пробовал несколько вариантов, и ничего не работает.

Я прочитал некоторые уже опубликованные вопросы и ответы, такие как: этот или этот но они, похоже, не отвечают на мой вопрос.

PS: я могу использовать C ++ 17, но это должно работать с любым компилятором.

Ответы [ 3 ]

2 голосов
/ 04 июня 2019

Предложение: попробуйте что-нибудь следующим образом

struct inner {
    using TA = std::tuple<As...>;
    using TB = std::tuple<Bs...>;

    template<bool dummy, typename UA = TA, typename E = void>
    struct problem;

    template<bool dummy, typename UA>
    struct problem<dummy, UA,
       std::enable_if_t<(std::tuple_size_v<UA> < std::tuple_size_v<TB>)>>
     { static constexpr auto val() { return 1; } };

    template<bool dummy, typename UA>
    struct problem<dummy, UA,
       std::enable_if_t<(std::tuple_size_v<UA> >= std::tuple_size_v<TB>)>>
     { static constexpr auto val() { return 0; } };
};

Я имею в виду ... принять во внимание, что SFINAE работает с тестами над параметром шаблона структуры / класса (или функции, или метода), который вы хотите включить/disable.

Проблема в исходном коде состоит в том, что тест SFINAE относится только к TA и TB, которые являются типами, определенными в структуре inner, которые содержат problem.Таким образом, тест зависит только от внешних параметров шаблона (As... и Bs...), а не от аргументов шаблона problem.

Добавление аргумента шаблона со значением по умолчанию для problem

// ..................VVVVVVVVVVVVVVVVV
template<bool dummy, typename UA = TA, typename E = void>
struct problem;

не меняет практического использования самого problem, но преобразует тест в std::enable_if

template<bool dummy, typename UA>
struct problem<dummy, UA, // ..........VV  UA, not TA
   std::enable_if_t<(std::tuple_size_v<UA> < std::tuple_size_v<TB>)>>
 { static constexpr auto val() { return 1; } };

в тест, который включает параметр шаблона самого problem.

1 голос
/ 04 июня 2019

Как заметил @unimportant: if-constexpr начиная с C ++ 17. Позволяет избавиться от dummy и многих других строк:

#include <type_traits>
#include <tuple>

template<typename ...As>
struct outer {
    template<typename ...Bs>
    struct inner {
        using TA = std::tuple<As...>;
        using TB = std::tuple<Bs...>;

        static constexpr auto val() { 
            if constexpr (std::tuple_size_v<TA> >= std::tuple_size_v<TB>) {
                return 0;
            }
            else {
                return 1;
            }
        }
    };
};

int main() {
    return outer<int, float>::inner<double>::val();
}

Программа «[4544] main.exe» вышла с кодом 0 (0x0).

1 голос
/ 04 июня 2019

Как предлагается в комментариях, использование if constexpr (часть C ++ 17) делает код более простым:

template<typename ...As>
struct outer {
    template<typename ...Bs>
    struct inner {
        using TA = std::tuple<As...>;
        using TB = std::tuple<Bs...>;

        struct problem
        {
            static constexpr auto val()
            {
                if constexpr (std::tuple_size<TA>::value < std::tuple_size<TB>::value)
                    return 1; // Complex function goes here
                else
                    return 0; // Other complex function (?) goes here
            }
        };
    };
};

Демо

...