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
?
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With