3

In the C++14 draft standard, [temp.param]/11 says:

If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter.

If you try compiling the following template, then the compiler will complain.

template< typename ...Args, void(*f)(Args...) > // ERROR
struct Bar
{};

But how does it work in this case?

template< typename F, F >
struct Bar;

template< typename ...Args, void(*f)(Args...) > // OK ???
struct Bar< void(*)(Args...), f >
{};

I can see that it has something to do with it being part of the specialization class template, but why?

The rule clearly states that it applies to a primary class template. Does this mean that the rules change for specializations?

I tried to search for this in the standard, but couldn't find anything. Can you please shine some light into this.

Barry
  • 286,269
  • 29
  • 621
  • 977
Constantinos Glynos
  • 2,952
  • 2
  • 14
  • 32
  • I'm sure someone will come up with more detailed answer, but it's mostly what you already got: A template specialization is neither a primary class template nor an alias template. – Not a real meerkat Sep 04 '18 at 23:49

1 Answers1

3

The rule clearly states that it applies to a primary class template. Does this mean that the rules change for specializations?

Yes. Quite simply because a specialization is not a primary class template. So if the wording was intended to apply to all template declarations, it would say so. Instead, the rule is very much intended to only apply to the primary class template (... and alias templates, which cannot be specialized). There is no such restriction for specializations.

This is fundamentally because it is not possible to provide any template arguments after a template parameter pack in the primary template, but it is definitely possible to do so in specializations. For instance, here's one way to concatenate two tuple specializations:

template <typename T, typename U>
struct tuple_concat;

template <typename... Ts, typename... Us> // <== parameter pack *after* parameter pack
struct tuple_concat<tuple<Ts...>, tuple<Us...>> {
    using type = tuple<Ts..., Us...>;
};

This is fine, it works, it's useful. But there's no benefit from being able to write stuff like this in a primary class/variable/alias template - so it's forbidden for simplicity.


As with all things C++, there is of course a footnote. You could have been able to provide a trailing defaulted template parameter that is used to trigger a substitution failure. But there are other ways you can go about solving that problem, and then we'll have Concepts soon anyway.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thank you very much for your answer. I understand now why it is valid C++ code. However, shouldn't it be specified somewhere in the standard? If I missed it, can you please point me in the right direction. In your example, wouldn't you create a tuple_concat object like this: **tuple_concat,std::tuple > foo;** If so, then why would you need that specialization at all? Just having the primary template works equally well. Sorry, I'm still a bit fuzzy about its usage now. Btw, I think you made a spelling mistake on the specialization class name - tuple_cat. – Constantinos Glynos Sep 05 '18 at 08:18
  • 1
    @Constantinos Not everything needs to be explicitly stated... otherwise the standard would have infinite length. And the point isn't to create a `tuple_concat` object, the point is that `tuple_concat,tuple>::type` is `tuple`... the primary does not work "equally well" for this task. – Barry Sep 05 '18 at 12:20
  • "...and then we'll have Concepts soon anyway". I hear that for more then 10 years now! Hope you are right! – Klaus Sep 05 '18 at 12:41
  • @Barry: Fair enough, but I believe that something like this should have been expressed in the standard - at least a mention. Ideally, they would also explain, in a couple of sentences, the reasons behind why this is being allowed as part of the specialization. I understand now that it is valid... but why? What's so special about the specialization that made them decide to allow it? Is it mere simplicity or is it yet another "special rule"? Apologies for the numerous questions - I do enjoy popping the hood on these things. – Constantinos Glynos Sep 05 '18 at 16:09
  • @ConstantinosGlynos The standard is _not_ a tutorial. – Barry Sep 05 '18 at 16:11
  • @ConstantinosGlynos Also, I tried to explain why - because specializations can meaningfully use arguments that follow parameter packs, but primary templates cannot. – Barry Sep 05 '18 at 16:11
  • @Barry: I didn't mean to imply that the standard is a tutorial. I just said that this case deserves a mention in the standard. Thanks for the explanation in your comment - the sentence "specializations can meaningfully use arguments" did the trick. :-) – Constantinos Glynos Sep 05 '18 at 16:32