11

Suppose we have the following code:

template<typename T>
class C
{};

template <typename T, template <typename> class Container>
void dummyMe(Container<T>&&)
{};

int main(int argc, char* argv[])
{
    C<int> c;
    dummyMe(c);
    return 0;
}

Which doesn't compile due to the first dummyMe argument being an rvalue-reference. Could someone explain me in Standardese why the template template parameters are not getting along with the forwarding references and why is it so in plain English.

P.S. I've stumbled on this and that questions but I do not see any real proofs in the answers.


An answer from the link above and the answer to this question assert that Container<T> can't be counted as a template parameter. And I see no reason why it is so. Let's make the example even simpler:

template <template <typename=int> class Container>
void dummyMe(Container<>&&)
{};

Now we have an example almost identical to the following:

template <typename Container>
void dummyMe(Container&&)
{};

But which is treated in a completely different fashion. Why? Why is Container<>&& can't be considered as the same thing to template <typename=int> class Container as Container&& to typename Container?

ixSci
  • 13,100
  • 5
  • 45
  • 79
  • 1
    What do you expect `Container` to be deduced as? `template C&`? That isn't a valid thing to put a reference qualifier on. – yuri kilochek Aug 21 '17 at 07:04
  • @yurikilochek, I expect the function to be `dummyMe&>(C&)`, nothing else. – ixSci Aug 21 '17 at 07:11
  • @ixSci this link you provided: https://stackoverflow.com/questions/32282705/a-failure-to-instantiate-function-templates-due-to-universal-forward-reference has an answer explaining **exactly** your case. Any reason why I shouldn't hammer this as a dupe? – bolov Aug 21 '17 at 07:19
  • @bolov, which one? One without a single quote from the standard or one with the irrelevant one? – ixSci Aug 21 '17 at 07:23
  • @ixSci this answer: https://stackoverflow.com/a/32283452/2805305 – bolov Aug 21 '17 at 07:29
  • @bolov, it has an explanation w/o any proofs from the Standard. I can't see why that answer is right. – ixSci Aug 21 '17 at 07:31
  • @ixSci read it all. Starting with the sentence "Now when you define a function g like" it deals exactly with you case. Explanations, examples, motivations. – bolov Aug 21 '17 at 07:31
  • One question per question please. Open a followup if you feel the need. – StoryTeller - Unslander Monica Aug 21 '17 at 07:58
  • Forwarding references only happen when the type being forwarded is deduced as part of resolving the function call. In other contexts, the same syntax is just a rvalue reference. – M.M Aug 21 '17 at 07:59

2 Answers2

12

The term "forwarding reference" is described at [temp.deduct.call/3](from C++17 draft n4659):

A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction).

In your example Container<T> is not a template parameter, it is a type you comprised from the template parameters T and Container. In order for the reference to be truly forwarding, you can use T&& only. While Conatiner is a template parameter, you can't have a reference to a template (the above paragraphs even mentions it explicitly). The type Container<T> is not the same as the template Container. It's an instantiated class.1

While you can use SFINAE to obtain a forwarding reference that can be bound only to the container type, I personally feel you're better off just overloading the function.

template <typename T, template <typename> class Container>
void dummyMe(Container<T>&&)
{}

template <typename T, template <typename> class Container>
void dummyMe(Container<T>&)
{}

1[temp.spec/2] - A class instantiated from a class template is called an instantiated class

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Why is it not a template parameter? Look at the [Standard](http://eel.is/c++draft/temp.param#nt:template-parameter), template template parameter is a template parameter. – ixSci Aug 21 '17 at 07:25
  • @ixSci - It's not a template parameter. It's a type comprised of the template parameters. You can't have a reference to a template anyway, you must instantiate it to obtain a class (a type). – StoryTeller - Unslander Monica Aug 21 '17 at 07:26
  • Well, the Standard says it **is** a template parameter and you say it is no. Who should I believe? – ixSci Aug 21 '17 at 07:28
  • Or you mean by adding brackets(<>) it became a different entity? If so could you please provide me with a quote from the Standard. – ixSci Aug 21 '17 at 07:30
  • 1
    @ixSci - You are misreading the standard. The template `Container` is indeed a parameter. `Container` is not a template, it's a type. And is not a parameter of the function template you defined. – StoryTeller - Unslander Monica Aug 21 '17 at 07:30
  • @ixSci - There's your quote. It's a new instantiated type. – StoryTeller - Unslander Monica Aug 21 '17 at 07:33
  • I see you logic but I still can't see how the quotes you provided proves anything. `Container` gets deduced when the function is called. Our Container is a template template parameter which is a template which can and should take another template parameter. It so happens we use `T` as its parameter. I do not see why it suddenly becomes some non-deducible entity. In fact it is deduced to be some type(`C` in the example) . Why it should be treated differently from a simple `T&&`? – ixSci Aug 21 '17 at 07:41
  • @ixSci `Container` is a template parameter but that's irrelevant. We are talking about a reference to `Container` which is most definitely not a template parameter. – n. m. could be an AI Aug 21 '17 at 07:48
  • @n.m., could you please elaborate on "definitely not a template parameter" why is that so? – ixSci Aug 21 '17 at 07:51
  • @ixSci There are exactly two template parameters to the `dummyMe` function template. One is `T` and the other is `Container`. It is a simle fact that `Container` is neither of those. – n. m. could be an AI Aug 21 '17 at 07:54
  • @ixSci - *"`Container` gets deduced when the function is called"*. No, because it's not a parameter of the template. The parameters of the template, and the only things that can be deduced are `T` and `Container`. They can indeed be deduced from the type of the argument being passed to the function. If you want an approachable explanation with standard quotes sprinkled in, watch [this two parter from CppCon 2016](https://www.youtube.com/watch?v=vwrXHznaYLA). – StoryTeller - Unslander Monica Aug 21 '17 at 07:56
  • @n.m, I updated the question. StoryTeller, I watched these videos are you sure they have a proper answer? – ixSci Aug 21 '17 at 07:59
  • @ixSci - They are the best explanation of how templates work that I've seen in my career. Despite the second hour being slightly rushed. They touch about deduction a fair bit. I daresay you may be due a re-watch, possibly a more attentive one. – StoryTeller - Unslander Monica Aug 21 '17 at 08:02
  • 1
    Even assuming - counterfactually - that `Container&&` were somehow a forwarding reference, that still doesn't help the OP. The way the forwarding reference "tweak" works is that it changes the argument type to `A&` and then deduces `T` against that to yield `T := A&`. But in that counterfactual world, the tweak means we would be deducing `Container` against `C&`, which yields...a deduction failure. – T.C. Aug 21 '17 at 08:34
  • @T.C., could you please elaborate on this? Why could not the rules be that way that template parameters and template template parameters behaved the same way? Like with simple `T&&` becoming `int&` why can't `Container&&` become `std::vector&`. So it influences only the "top" type which is `Container` not its `` part. At least it is what to be expected in the first place, as I see it. That's why the question, really. To me the behavior, no matter how standard conforming it is, is really counter-intuitive. – ixSci Aug 21 '17 at 09:08
7

An answer from the link above and the answer to this question assert that Container<T> can't be counted as a template parameter

What is or is not a template parameter is not subject to much interpretation. It is clearly defined in [temp.param]:

template-parameter: 
    type-parameter 
    parameter-declaration 
 type-parameter: 
    type-parameter-key ...(opt) identier (opt)
    type-parameter-key identier(opt) = type-id 
    template < template-parameter-list > type-parameter-key ...(opt) identier(opt)
    template < template-parameter-list > type-parameter-key identier(opt) = id-expression 
type-parameter-key: 
    class
    typename

It is clear from these production rules that dummyMe has exactly two template parameters: typename T and template <typename> class Container. Identifiers that name each of these parameters are T and Container. T names the first parameter and Container names the second one. Container<T> is not an identifier and names neither of the two.

T.C.
  • 133,968
  • 17
  • 288
  • 421
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • But why? `Container` is something that should be accompanied by the brackets (<>) so we gave the `Container` those brackets. Why it became some different entity? – ixSci Aug 21 '17 at 08:16
  • I have quoted simple undisputable facts from the standard, exactly as requested in the question. If you want to ask why the language doesn't define template parameters the way you like, please ask a different question. – n. m. could be an AI Aug 21 '17 at 08:41
  • "Container is something that should be accompanied by the brackets (<>)". This is of course not true. You can pass `Container` as is and without brackets to further templates (ones that accept a suitable template template.parameter). – n. m. could be an AI Aug 21 '17 at 08:45
  • Finally got it. It really can't be a template parameter since the syntax is different. Thank you! – ixSci Aug 21 '17 at 08:58