Mar*_*son 16 c++ alias templates c++14 forwarding-reference
请考虑以下以下代码段:
template <class T>
using identity = T;
template <class T>
void foo(identity<T>&&) {}
int main()
{
int i{};
foo(i);
}
Run Code Online (Sandbox Code Playgroud)
i是一个左值,因此如果foo声明一个转发引用参数,它应该编译.但是,如果identity<T>&&转为int&&,则应该引发错误.
该代码在GCC 6.0.0(演示)中编译.
代码无法在Clang 3.7.0(演示)中编译,并显示错误消息:
error: no known conversion from 'int'
to 'identity<int> &&' (aka 'int &&') for 1st argument
Run Code Online (Sandbox Code Playgroud)
哪一个是对的?
考虑以下代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
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.[例如:
Run Code Online (Sandbox Code Playgroud)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- 末端的例子]
这似乎与您的示例无关,但它实际上表明在某些情况下仍然会考虑模板ID的初始形式,与取代的type-id无关.我想这开启了identity<T>&&实际上可能不会被视为转发参考的可能性.
这个领域似乎在标准中没有明确规定.这表明处理类似问题的公开问题的数量,在我看来都属于同一类别:在什么情况下,在实例化时应该考虑模板id的初始形式,即使它应该被替换为遇到时立即对应的type-id.见1980年,2021年和2025年的问题.甚至问题1430和1554也可被视为处理类似问题.
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();
Run Code Online (Sandbox Code Playgroud)
附注:
CWG认为这两个声明不应该是等同的.
(CWG - 核心工作组)
类似的推理可能适用于您的示例,identity<T>&&不等同于转发引用.这甚至可以具有实用价值,作为一种直接的方式来避免转发引用的贪婪,只要你想要的是对推导的T的右值引用.
所以,我认为你提出了一个非常有趣的问题.你的例子可能值得作为1980年发行的注释添加,以确保在起草决议时考虑到这一点.
在我看来,你的问题的答案是,现在,一个响亮的"谁知道?".
更新:在对另一个相关问题的评论中,Piotr S.指出了问题1700,该问题被关闭为"不是缺陷".它指的是该问题中描述的非常相似的案例,并包含以下基本原理:
由于函数参数的类型相同,无论是直接写入还是通过别名模板,在两种情况下都必须以相同的方式处理推导.
我认为它同样适用于此处讨论的案例,并且现在解决了这个问题:所有这些表单应该被视为等同于转发引用.
(看看这是否由其他公开问题的解决方案间接改变将是有趣的,但它们主要是处理替换失败而不是单独扣除,所以我猜这种间接影响是不太可能的.)
所有标准参考文献都是当前的工作草案,N4431,最终C++ 14之后的第二稿.
请注意,[14.5.7p3]的引用是最近添加的,包含在最终的C++ 14版本之后,作为DR1558的分辨率.我认为我们可以期待在这个领域进一步增加,因为其他问题以这种或那种方式得到解决.
在那之前,可能值得在ISO C++标准 - 讨论组中提出这个问题; 这应该引起合适的人的注意.
它不是转发参考.C++ 14(n4140)14.8.2.1/3(强调我的):
...如果
P是对cv-unqualified模板参数的rvalue引用,并且参数是左值,则使用类型"左值引用A"代替A类型推导.
这是标准的一部分,它指定了转发引用的工作方式.P,函数参数的类型是"rvalue reference to identity<T>" 类型.identity<T>是模板参数的类型,但它本身不是模板参数,因此转发参考扣除规则不适用.
我们还可以看看14.5.7/2对别名模板的看法:
当模板id是指一个别名模板的专业化,它是等效于相关联的类型 通过其置换而得模板参数为模板参数在类型ID别名模板.
所以取代别名是相当于型的T,但14.8.2.1/3读"参考...模板参数",而不是"参考...模板参数的类型."
| 归档时间: |
|
| 查看次数: |
399 次 |
| 最近记录: |