身份别名模板可以作为转发参考吗?
Can an identity alias template be a forwarding reference?
考虑下面的代码片段:
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
哪一个是正确的?
这不是转发参考。 C++14 (n4140) 14.8.2.1/3(强调我的):
... If P
is an rvalue reference to a cv-unqualified
template parameter and the argument is an lvalue, the type “lvalue reference to A
” is used in
place of A
for type deduction.
这是指定转发引用如何工作的标准部分。 P
,函数参数的类型,是"rvalue reference to identity<T>
."类型 identity<T>
是模板参数的类型,但本身不是模板参数,所以转发引用推导规则没有不适用。
我们还可以看看 14.5.7/2 对别名模板的看法:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type
obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias
template.
因此替换的别名等同于 T
的 type,但 14.8.2.1/3 读取 "reference to ... template parameter," 而不是 "reference to ... the type of a template parameter."
考虑这段代码:
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
是一个 class 模板,它的形式可以在不查看其定义的情况下与函数参数的类型匹配,从而允许编译器推断出 T
的模板参数。但是,这里我们有一个别名模板; T
不能推导为 int
或 int&
或其他任何东西,除非 identity<T>
被 T
替换。否则,我们要匹配什么?一旦替换完成,函数参数就变成了转发引用。
以上所有内容都支持将 identity<T>&&
(和 identity<T&&>
)视为等同于转发引用的想法。
然而,似乎还有更多的事情要做,即立即用相应的类型 ID 替换别名模板 ID。 [14.5.7p3] 段说:
However, if the template-id is dependent, subsequent template argument
substitution still applies to the template-id. [ Example:
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 的初始形式,而与替换的类型 ID 无关。我想这打开了 identity<T>&&
毕竟实际上不能被视为转发引用的可能性。
该区域似乎在标准中未指定。这显示了处理类似问题的未解决问题的数量,在我看来都属于同一类别:在什么情况下应该在实例化时考虑模板 ID 的初始形式,即使它应该被替换遇到立即对应的type-id。 See issues 1980, 2021 and 2025. Even issues 1430 and 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 felt that these two declarations should not be equivalent.
(CWG - 核心工作组)
类似的推理可以适用于您的示例,使 identity<T>&&
不等同于转发引用。这甚至可能具有实用价值,作为一种直接的方法来避免转发引用的贪婪,当你想要的只是对推导的 T 的右值引用时。
所以,我认为您提出了一个非常有趣的问题。您的示例可能值得作为注释添加到 issue 1980,以确保在起草决议时考虑到这一点。
在我看来,目前您的问题的答案是响亮的 "who knows?"。
更新:在对其他相关 , Piotr S. pointed out issue 1700 的评论中,已作为 "not a defect" 关闭。它指的是该问题中描述的非常相似的案例,并包含以下基本原理:
Because the types of the function parameters are the same, regardless of whether written directly or via an alias template, deduction must be handled the same way in both cases.
我认为它同样适用于这里讨论的案例,并且暂时解决了这个问题:所有这些表格都应该被视为等同于转发参考。
(有趣的是看看其他未决问题的决议是否间接改变了这一点,但它们主要处理替换失败而不是推导本身,所以我想这种间接影响不太可能。 )
所有标准参考均针对当前工作草案 N4431,即最终 C++14 之后的第二稿。
请注意,来自 [14.5.7p3] 的引用是最近添加的,作为 DR1558 的决议包含在最终 C++14 版本之后。我认为随着其他问题以某种方式得到解决,我们可以期待在这一领域有更多的补充。
到那时,可能值得在 ISO C++ Standard - Discussion 组中提出这个问题;这应该引起合适的人的注意。
考虑下面的代码片段:
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
哪一个是正确的?
这不是转发参考。 C++14 (n4140) 14.8.2.1/3(强调我的):
... If
P
is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type “lvalue reference toA
” is used in place ofA
for type deduction.
这是指定转发引用如何工作的标准部分。 P
,函数参数的类型,是"rvalue reference to identity<T>
."类型 identity<T>
是模板参数的类型,但本身不是模板参数,所以转发引用推导规则没有不适用。
我们还可以看看 14.5.7/2 对别名模板的看法:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.
因此替换的别名等同于 T
的 type,但 14.8.2.1/3 读取 "reference to ... template parameter," 而不是 "reference to ... the type of a template parameter."
考虑这段代码:
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
是一个 class 模板,它的形式可以在不查看其定义的情况下与函数参数的类型匹配,从而允许编译器推断出 T
的模板参数。但是,这里我们有一个别名模板; T
不能推导为 int
或 int&
或其他任何东西,除非 identity<T>
被 T
替换。否则,我们要匹配什么?一旦替换完成,函数参数就变成了转发引用。
以上所有内容都支持将 identity<T>&&
(和 identity<T&&>
)视为等同于转发引用的想法。
然而,似乎还有更多的事情要做,即立即用相应的类型 ID 替换别名模板 ID。 [14.5.7p3] 段说:
However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id. [ Example:
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 的初始形式,而与替换的类型 ID 无关。我想这打开了 identity<T>&&
毕竟实际上不能被视为转发引用的可能性。
该区域似乎在标准中未指定。这显示了处理类似问题的未解决问题的数量,在我看来都属于同一类别:在什么情况下应该在实例化时考虑模板 ID 的初始形式,即使它应该被替换遇到立即对应的type-id。 See issues 1980, 2021 and 2025. Even issues 1430 and 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 felt that these two declarations should not be equivalent.
(CWG - 核心工作组)
类似的推理可以适用于您的示例,使 identity<T>&&
不等同于转发引用。这甚至可能具有实用价值,作为一种直接的方法来避免转发引用的贪婪,当你想要的只是对推导的 T 的右值引用时。
所以,我认为您提出了一个非常有趣的问题。您的示例可能值得作为注释添加到 issue 1980,以确保在起草决议时考虑到这一点。
在我看来,目前您的问题的答案是响亮的 "who knows?"。
更新:在对其他相关
Because the types of the function parameters are the same, regardless of whether written directly or via an alias template, deduction must be handled the same way in both cases.
我认为它同样适用于这里讨论的案例,并且暂时解决了这个问题:所有这些表格都应该被视为等同于转发参考。
(有趣的是看看其他未决问题的决议是否间接改变了这一点,但它们主要处理替换失败而不是推导本身,所以我想这种间接影响不太可能。 )
所有标准参考均针对当前工作草案 N4431,即最终 C++14 之后的第二稿。
请注意,来自 [14.5.7p3] 的引用是最近添加的,作为 DR1558 的决议包含在最终 C++14 版本之后。我认为随着其他问题以某种方式得到解决,我们可以期待在这一领域有更多的补充。
到那时,可能值得在 ISO C++ Standard - Discussion 组中提出这个问题;这应该引起合适的人的注意。