模板模板参数和转发参考

ixS*_*Sci 10 c++ language-lawyer

假设我们有以下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

由于第一个dummyMe参数是rvalue-reference ,因此无法编译.有人可以在Standardese中解释为什么模板模板参数与转发参考不相符,为什么它用简单的英语.

PS我偶然发现了这个那些问题,但我没有在答案中看到任何真实的证据.


上面链接的答案和这个问题的答案断言Container<T>不能算作模板参数.我认为没有理由这样做.让我们的例子更简单:

template <template <typename=int> class Container>
void dummyMe(Container<>&&)
{};
Run Code Online (Sandbox Code Playgroud)

现在我们有一个几乎与以下相同的示例:

template <typename Container>
void dummyMe(Container&&)
{};
Run Code Online (Sandbox Code Playgroud)

但是以完全不同的方式对待它.为什么?为什么Container<>&&不能被视为同样的事情template <typename=int> class Container作为Container&&typename Container

Sto*_*ica 11

术语"转发参考"在[temp.deduct.call/3](来自C++ 17 draft n4659)中描述:

转发引用是对cv-unqualified 模板参数的rvalue引用,该 参数不表示类模板的模板参数(在类模板参数推导期间).

在你的例子Container<T>不是一个模板参数,它是你从模板参数由类型TContainer.为了使引用真正转发,您只能使用T&&.虽然Conatiner是模板参数,但您不能引用模板(上面的段落甚至明确提到它).该类型 Container<T>是不一样的模板 Container.这是一个实例化的类.1

虽然您可以使用SFINAE获取只能绑定到容器类型的转发引用,但我个人觉得您最好只是重载该功能.

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

template <typename T, template <typename> class Container>
void dummyMe(Container<T>&)
{}
Run Code Online (Sandbox Code Playgroud)

1 [temp.spec/2] - 从类模板实例化的类称为实例化类


n. *_* m. 6

上面链接的答案和这个问题的答案断言Container<T>不能算作模板参数

什么是或不是模板参数不需要太多解释.它明确定义[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
Run Code Online (Sandbox Code Playgroud)

从这些生产规则中可以清楚地看出,dummyMe它只有两个模板参数:typename Ttemplate <typename> class Container.命名每个参数的标识符是TContainer.T命名第一个参数并Container命名第二个参数.Container<T>不是标识符,也不是两者的名称.