Clang и GCC не согласны с автоматическим спецификатором для параметра нестандартного шаблона в литье С++ 17

У меня в основном есть класс, который зависит от параметра шаблона, отличного от типа. Я определил кастинг таким образом, чтобы объект шаблона N мог преобразовать в другой из M У меня есть минимальный пример, который может воспроизвести ситуацию:

template<auto Integral>
class Test{
    public:
        typedef decltype(Integral) value_type;
        static constexpr value_type N = Integral;

        constexpr Test (const value_type& x = 0);

        template<auto Integral2>
        constexpr explicit operator Test<Integral2>() const;
    private:
        value_type n;
};

template<auto Integral>
constexpr Test<Integral>::Test (const value_type& x){
    if (x < 0){
        n = N - (-x)%N;
    }
    else{
        n = x%N;
    }
}

template<auto Integral> template<auto Integral2>
constexpr Test<Integral>::operator Test<Integral2>() const{
    return Test<Integral2>(n%(Test<Integral2>::N));
}

Я компилирую GCC 7.2.0 и Clang 5.0.0 в Ubuntu 16.04 LTS с флагами -O2 -std=c++17.

Проблема в том, что я все время скомпилировал с помощью g++, и все работало, как ожидалось, но затем я попробовал clan g++, чтобы проверить, все ли еще компилируется.

К моему удивлению, это было не так, поскольку клан g++ жаловался на некоторые части, которые g++ этого не делал. Вы можете проверить представление diff в Compiler Explorer.

Одним из сообщений об ошибках, которые создает клан g++, является:

error: out-of-line definition of 'operator Test<Integral2>' does not match any declaration in 'Test<Integral>'

Это заставляет меня думать, что клан g++ не нашел "правильной" декларации о конверсии, но я не знаю.

Примечание. Эта первая ошибка появляется только при отмене объявления из определения. В противном случае это кажется правильным.

Это вторая ошибка, создаваемая кланом g++:

error: a non-type template parameter cannot have type 'auto'

Но это меня еще больше удивляет, потому что ошибка говорит о том, что должно быть допустимо в С++ 17. Наверное, мне что-то не хватает, потому что это просто не имеет смысла для меня.

Имейте в виду, что эта вторая ошибка появляется только в случае этого преобразования. В любом месте в фактическом коде появляется ошибка (хотя есть еще много параметров шаблона auto не-типа).

На данный момент у меня есть некоторые вопросы:

  • Что вызывает ошибку в случае компилятора clang?
  • Какой из них правильный, в соответствии со стандартом?

Ответы

Ответ 1

Это ошибка clang. Здесь короткое воспроизведение:

template <int A>
struct X {
    template <auto B>
    X<B> foo();
};

template <int A>
template <auto B>
X<B> X<A>::foo() {
    return {};
}

Если auto B заменяется на int B, clang принимает его. gcc принимает его как есть. Для clang это только проблема с вложенными объявлениями template. Нет ничего о auto в качестве параметра шаблона шаблона-заполнителя, который не позволял бы ему использовать что-то вне линии.

При подаче новой ошибки clang я нашел 35655 с еще более коротким воспроизведением:

template<typename>
struct S {
    template<auto n>
    static void f() {
        +n;
    }
};

который терпит неудачу:

source.cpp:5:3: error: invalid argument type 'auto' to unary expression
                +n;