Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template template parameters and forwarding references

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?

like image 396
ixSci Avatar asked Aug 21 '17 06:08

ixSci


2 Answers

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

like image 60
StoryTeller - Unslander Monica Avatar answered Oct 11 '22 06:10

StoryTeller - Unslander Monica


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.

like image 38
n. 1.8e9-where's-my-share m. Avatar answered Oct 11 '22 04:10

n. 1.8e9-where's-my-share m.