Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clang and GCC disagree in auto specifier for non-type template parameter in a casting C++17

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?
like image 507
Francisco Gallego Salido Avatar asked Jan 05 '18 02:01

Francisco Gallego Salido


People also ask

Why use Variadic templates in C++11?

With C++11 we got variadic templates which is a great feature, especially if you want to work with a variable number of input parameters to a function. For example, previously (pre C++11) you had to write several different versions of a function (like one for one parameter, another for two parameters, another for three params… ).

Do we need to write type explicitly in C++17?

with C++17 it’s a bit simpler: So no need to write Type explicitly. As one of the advanced uses a lot of papers/blogs/talks point to an example of Heterogeneous compile time list: Before C++17 it was not possible to declare such list directly, some wrapper class would have to be provided first.

How good is C++17 for template writing?

With C++17 we get a few nice improvements: some are quite small, but also there are notable features as well! All in all, the additions should significantly improve writing template code. BTW: if you’re really brave you can still use concepts!

What is C++17 template deduction?

C++17 filled a gap in the deduction rules for templates. Now the template deduction can happen for standard class templates and not just for functions. For instance, the following code is (and was) legal: Because std::make_pair is a template function (so we can perform template deduction). But the following wasn’t (before C++17)


1 Answers

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;
like image 120
Barry Avatar answered Sep 22 '22 06:09

Barry