Clang and GCC disagree in auto specifier for non-t

2020-06-08 02:50发布

问题:

I basically have a class that depends on a non-type template parameter. I defined a casting so an object of non-type template parameter N can convert to another of M. I have a minimal example that can reproduce the situation:

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));
}

I'm compiling using GCC 7.2.0 and Clang 5.0.0 in Ubuntu 16.04 LTS with the flags -O2 -std=c++17.

The problem is that I was compiling all the time using g++ and everything worked as expected, but then I tried clang++ to check if everything was still compiling fine.

To my surprise, that wasn't the case, as clang++ complained about some parts that g++ didn't. You can check a diff view in Compiler Explorer.

One of the error messages that clang++ produces is:

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

This one makes me think that clang++ didn't find a "right" declaration of the conversion, but I don't know.

Note: This first error only appears when separating the declaration from the definition. Otherwise, it seems to be correct.

This is the second error that clang++ produces:

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

But this one surprises me even more because the error tells something that is supposed to be valid in C++17. Probably I'm missing something here because this just doesn't make sense to me.

Keep in mind that this second error only appears in the case of this conversion. Anywhere in the actual code gives the error (even though there are many more auto non-type template parameters).

At this point, I have some questions:

  • What is producing the error in the case of the clang compiler?
  • Which one is correct, according to the standard?

回答1:

This is a clang bug. Here's a short reproduction:

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

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

If auto B is replaced by int B, clang accepts it. gcc accepts it as-is. For clang, this is only a problem with the nested template declarations. There's nothing about auto as a placeholder template non-type parameter that would prevent it from being used to define something out-of-line.

While filing a new clang bug, I found 35655, with an even shorter reproduction:

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

which fails with:

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