请考虑以下代码段:
template <class T>
using identity = T;
template <class T>
void foo(identity<T>&&) {}
int main()
{
int i{};
foo(i);
}
i
是一个左值,因此如果foo
声明转发引用参数,它应该编译。但是,如果identity<T>&&
变为int&&
,则应该引发错误。
代码在GCC 6.0.0(demo)中编译。
代码无法在Clang 3.7.0(demo)中编译,并显示错误消息:
error: no known conversion from 'int'
to 'identity<int> &&' (aka 'int &&') for 1st argument
哪一个是对的?
答案 0 :(得分:8)
考虑以下代码:
template<class T> using identity = T;
template<class T> void foo(identity<T>&&) { } //#1
template<class T> void foo(T&&) { } //#2
int main()
{
int i{};
foo(i);
}
GCC和Clang都拒绝它,因为#2
是对#1
的重新定义。如果它们实际上是相同的模板,我们可以期望#1
的行为方式与#2
完全相同,这意味着identity<T>&&
应该充当转发参考。遵循这个逻辑,我们不知道哪一个是正确的,但GCC至少是一致的。
这也与[14.5.7p2]标准中非常相似的例子一致。
我们还应该考虑模板参数推导在这种情况下可以起作用的方式。如果identity
是类模板,则可以将其形式与函数参数的类型进行匹配,而不查看其定义,从而允许编译器推导出T
的模板参数。但是,这里有一个别名模板;除非T
替换int
,否则无法将int&
推断为identity<T>
或T
或其他任何内容。否则,我们匹配什么?替换完成后,函数参数将成为转发参考。
以上所有都支持将identity<T>&&
(和identity<T&&>
)视为等同于转发参考的想法。
但是,似乎还有更多的东西,即使用相应的type-id立即替换别名template-id。第[14.5.7p3]段说:
但是,如果template-id是依赖的,则后续的模板参数 替换仍然适用于template-id。 [例如:
template<typename...> using void_t = void; template<typename T> void_t<typename T::foo> f(); f<int>(); // error, int does not have a nested type foo
-end example]
这似乎与您的示例无关,但它实际上表明在某些情况下仍然会考虑模板ID的初始形式,与取代的type-id无关。我想这可能会导致identity<T>&&
实际上不能被视为转发参考。
该标准似乎没有明确规定。这表明处理类似问题的公开问题的数量,在我看来都属于同一类别:在什么情况下,在实例化时应该考虑模板id的初始形式,即使它应该被替换为遇到时立即对应的type-id。查看问题1980,2021和2025。即使问题1430和1554也可以被视为处理类似的问题。
特别是,issue 1980包含以下示例:
template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();
附注:
CWG认为这两个声明不应该相同。
(CWG--核心工作组)
类似的推理可能适用于您的示例,使identity<T>&&
不等同于转发引用。这甚至可以具有实用价值,作为一种直接的方法,当你想要的只是对推导出的T的右值引用时,避免转发引用的贪婪。
所以,我认为你提出了一个非常有趣的问题。您的示例可能值得添加为issue 1980的注释,以确保在起草决议时考虑到这一点。
在我看来,你的问题的答案是,现在,一个响亮的“谁知道?”。
更新:在对其他人的评论中,相关的question,Piotr S.指出了issue 1700,其被视为“非缺陷”。它指的是该问题中描述的非常相似的案例,并包含以下基本原理:
因为函数参数的类型是相同的,无论是直接写还是通过别名模板,在两种情况下都必须以相同的方式处理推论。
我认为它同样适用于此处讨论的案例,并且现在解决了这个问题:所有这些表格应该被视为等同于转发参考。
(看看这是否由其他公开问题的解决方案间接改变将是有趣的,但它们主要处理替代失败而不是自身的推论,所以我猜这种间接影响是不太可能的。)< / p>
所有标准参考文献都是当前的工作草案,N4431,最终C ++ 14之后的第二稿。
请注意,[14.5.7p3]中的引用是最近添加的,包含在最终的C ++ 14版本之后,作为DR1558的分辨率。我认为我们可以期待在这个领域进一步增加,因为其他问题以这种或那种方式得到解决。
在此之前,可能值得在ISO C++ Standard - Discussion小组中提出这个问题;这应该引起合适的人的注意。
答案 1 :(得分:7)
它不是转发参考。 C ++ 14(n4140)14.8.2.1/3(强调我的):
...如果
P
是对右值引用 模板参数,参数是左值,类型“对 A
的左值引用”用于A
的地方类型扣除。
这是标准的一部分,它指定了转发引用的工作方式。 P
是函数参数的类型,类型为&#34;对identity<T>
的右值引用。&#34; identity<T>
是模板参数的类型,但它本身不是模板参数,因此转发参考扣除规则不适用。
我们还可以看看14.5.7 / 2对别名模板的评价:
当 template-id 引用别名模板的特化时,它等同于关联的类型 通过将 template-arguments 替换为别名的 type-id 中的 template-parameters 获得 模板。
因此,替换别名等同于T
的类型,但14.8.2.1/3读取&#34;引用...模板参数,&#34;不是&#34;引用...模板参数的类型。&#34;