Разное поведение в компиляторах для std :: enable_if (зависит от параметров шаблона внешнего класса) - PullRequest
0 голосов
/ 03 января 2019

У меня есть вложенный (Inner) класс, для которого я хочу enable_if конструктор, в зависимости от того, сколько параметров шаблона (Args) имеет включающий класс (Outer).

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

#include <tuple>
#include <type_traits>


template <typename... Args>
struct Outer {

    struct Inner {
        Inner(const Outer* out, Args... vals) 
            : outer(out)
            , values(vals...)
         {}

         // This ctor should be enabled only if Args are non-empty
         template <typename = std::enable_if_t<(sizeof...(Args) > 0)>>
         Inner(const Outer* out)
            : outer(out)
         {}

        const Outer* outer;
        std::tuple<Args...> values;
    };

};

int main()
{
    Outer<int, int> o1;
    Outer<int, int>::Inner i1(&o1, 1, 2);
    Outer<int, int>::Inner i11(&o1);

    Outer<> o2;
    Outer<>::Inner i2(&o2);
    Outer<>::Inner i21(nullptr);

}

Показано на Godbolt: https://godbolt.org/z/lsivO9

забавные результаты:

  • GCC 8.2 -std=c++17 - не удалось скомпилировать
  • GCC trunk -std=c++17 - ОК
  • MSVC 19.14 /std:c++17 /permissive- - ОК
  • MSVC 19.16 /std:c++17 /permissive- - ОК
  • clang 7 -std=c++17 - НЕУДАЧИТЬ компилировать
  • clang trunk -std=c++17 - НЕУДАЧИТЬ компилировать

Итак, вопросы:

  • Является ли Args... класса Outer в непосредственном контексте класса Inner?
  • Является ли приведенный выше пример правильным?
  • Какой компилятор подходит?
  • Почему GCC начал вести себя по-разному с транком?

Ответы [ 2 ]

0 голосов
/ 03 января 2019
template <typename = std::enable_if_t<(sizeof...(Args) > 0)>>
     Inner(const Outer* out)

Когда Args пусто, это

template <typename = std::enable_if_t<false>>
     Inner(const Outer* out)

SFINAE применяется только к функциям, когда SFINAE зависит от параметров шаблона функции. Здесь это не так. Итак, серьезная ошибка правильна.

Это может быть случай, когда диагностика не требуется, и ваша программа по-прежнему плохо сформирована (поэтому компилятор может делать все что угодно). Дразнить это сложно, и, поскольку есть простой обходной путь, вы можете сделать это.

template <std::size_t N = sizeof...(Args), typename = std::enable_if_t<(N > 0)>>

или мой любимый

template <std::size_t N = sizeof...(Args), std::enable_if_t<(N > 0), bool> = true>

, который лучше работает при перегрузках.

Однако в вашем конкретном случае первый ctor становится вторым:

    Inner(const Outer* out, Args... vals) 
        : outer(out)
        , values(vals...)
     {}

когда Args... пусто, поэтому я не вижу здесь смысла.

0 голосов
/ 03 января 2019

Необходимо сделать std::enable_if зависимым от параметра шаблона конструктора

template <std::size_t N = sizeof...(Args), std::enable_if_t<(N > 0), bool> = true>
Inner(const Outer* out)
: outer(out)
{}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...