Clang vs G++ disagree on class template number of arguments and template template parameter redeclaration
Asked Answered
W

1

8

In the following example, Abstract is a class template whose first parameter is a type and the second parameter is another template taking a bool along with any number of args.

template<bool,typename>
struct Default;

template< typename T = void,
          template<bool,typename ...> class = Default>
struct Abstract;

template<typename T>
struct Abstract<T>
{};

template<typename T, template<bool> class C>
struct Abstract<T,C> : Abstract<T>
{};

int main()
{}

The outputs from Clang and C++ are the following:

Clang: http://rextester.com/BJSW46677

error: class template partial specialization does not specialize any template argument;

G++ http://rextester.com/MDN65674

OK

So, I decided to go clang-friendly and added a third parameter in Abstract's declaration.

template< typename T = void,
          template<bool,typename ...> class = Default,
          typename = void >
struct Abstract;

Now, both Clang and G++ were fine with this. I guess the reason Clang was complaining, was because the specialization did not really specialize anything. But that's not true. It specializes for the number of arguments.

Next, I added another specialization for the template template parameter. The example looks like this:

template<bool,typename>
struct Default;

template< typename T = void,
          template<bool,typename ...> class = Default,
          typename = void >
struct Abstract;

template<typename T>
struct Abstract<T>
{};

template<typename T, template<bool> class C>
struct Abstract<T,C> : Abstract<T>
{};

template<typename T, template<bool,typename> class G>
struct Abstract<T,G> : Abstract<T>
{};

int main()
{}

The outputs from Clang and C++ are the following:

Clang: http://rextester.com/LJIOC38789

error: redefinition of Abstract<type-parameter-0-0, C, void> note: previous definition is struct Abstract<T,C> : Abstract<T>

G++: http://rextester.com/TSDRZ44717

OK - (didn't need the third parameter in the declaration either)

I don't think that Clang is right here because the third specialization is valid for the template template argument, and the variadic template parameter allows me to specialize for any number of arguments. However, I'm not sure.

Question: Which compiler is the buggy one? Why? It would be really nice to get a quote from the spec and more clarity on the subject.

Wanids answered 20/2, 2018 at 18:22 Comment(12)
The essence of your question is in this question #48894428Lisk
@NikitaKniazev Please correct me, but I believe that the question you refer to, talks about ambiguity between the primary variadic template, and its specialization. I don't think it's an ambiguity btw. The C++ Templates - The Complete Guide 2nd edition, p351 says: "Moreover, it is even possible that the number of explicitly written template arguments can differ from the number of template parameters in the primary template. This can happen both with default template arguments and, in a far more useful manner, with variadic templates". There's also an example, which doesn't fit in the comments.Wanids
you are (like in the question I've linked) trying to specialize by a template template class and assume that number of template parameters in template template class is taken into account when compiler tries to find the best match for a partial specialization.Lisk
@NikitaKniazev Sorry.. This is becoming very stressful. I just put the following example through godbolt to see what the compiler does, and everything seems to be ok ( godbolt.org/g/4K6euj ). Also, have a look at this textester example ( rextester.com/KYZ39240 ). If this is a G++ bug, then it's a happy bug. But I don't see why the specialization would be ill-formed! Is it explicitly mentioned somewhere in the C++ Standard?Wanids
@NikitaKniazev Furhtermore, I found this in the C++ Templates book 2nd ed.: "Variadic template template parameters are an exception to the pre-C++17 “exact match” rule described above and offer a solution to this limitation: They enable more general matching against template template arguments. A template template parameter pack can match zero or more template parameters of the same kind in the template template argument". That means that the specialization is not ill-formed. The book also shows a similar example to the one I send in my previous comment. I think G++ got it right in this case.Wanids
@ConstantinosGlynos Could you give us more info about the context in which you're trying to use this partial specialization? Some code that would benefit from it in practice? Since this is no longer valid in C++17, I'm curious about how much impact this may have.Stook
@Stook Unfortunately this is as much info as I can release.. I can however create different examples if that would help! The idea is simple and sound. All it does, is specialize for the number of args instead of explicit types. It's very strange that you say it's no longer valid in C++17! I personally don't know C++17, but I can only say that the C++ Templates book I'm referring to above, is written for C++11,14 and 17. Could you please point me to the documentation that invalidates the above code in C++17?Wanids
We can come up with many synthetic examples, of course, but I'm interested in seeing some real code that is affected by this and whether it could be done differently. The answer and comments to the question linked above indicate why this no longer works in C++17. The first quote you gave from the Templates Book refers to specializing on the number of arguments given to the template, not the number of parameters of a template template-argument. The second quote hints at the change made in C++17 (the additional flexibility in matching) that has the side-effect of making your example invalid.Stook
@Stook I see what you mean, but this question is not really about finding a better solution to the problem. For example, I can do it without the variadic template template parameter and simply set a large number of typenames in the template template parameter (20 typenames) and leave it as a limitation - that would satisfy both compilers, but I don't like limitations. Btw, I think that's how it was done prior to variadic templates, which would solve the problem in this case. What I'm asking, is which compiler follows the standard and which one has the bug.Wanids
@Stook The quotes come from chapter 12.3.4. It says: "C++17 relaxed the matching rule to just require that the template template parameter be at least as specialized as the corresponding template template argument.". That's where my second quote comes in and says: "Variadic template template parameters are an exception to the pre-C++17 “exact match” rule described above". I cannot really fit the entire chapter in the comments, but it doesn't say anywhere that this is not allowed in 17. Besides, I tried running the same examples in C++17 and I got the same results.Wanids
In terms of alternatives, I'd rather consider a solution that doesn't use partial specialization at all. Quotes: of course the book won't say that this is not allowed in C++17; it can't cover all the possible template constructs, and this one is very specific and uncommon. The conclusion is derived by following the steps in the standard, as explained in the answer linked above.Stook
Let us continue this discussion in chat.Stook
B
1

As pointed out in the comments, the first part of the question—namely, whether it's allowed to specialize on a template template argument that is a variadic template in the primary template—is essentially the same as this other question. If you read my answer to that question, it contains a summary of how the partial ordering rules for partial specializations work. In your case in particular, the problem is that since C++17, a template<bool,typename ...> class parameter can accept a template<bool> class argument and vice versa; that means that in C++17 and later, the partial ordering rules come to the conclusion that the partial specialization you wrote is not more specialized than the primary template, which makes the program ill-formed.

(FWIW, the latest version of Clang trunk available on Godbolt accepts the specialization, and I would expect CWG2398 to be eventually resolved in a way that makes this code well-formed.)

As for the second part of your question, regarding the pair of partial specializations:

template<typename T, template<bool> class C>
struct Abstract<T,C> : Abstract<T>
{};

template<typename T, template<bool,typename> class G>
struct Abstract<T,G> : Abstract<T>
{};

the claim by some versions of Clang that the second one is a "redeclaration" of the first one is simply nonsense; it is presumably a bug in those versions. This is fixed in trunk.

Bloodletting answered 13/3, 2023 at 0:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.