身份别名模板是否可以作为转发引用
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(演示)中编译。
代码无法在 Clang 3.7.0(演示)中编译,并显示错误消息:
error: no known conversion from 'int'
to 'identity<int> &&' (aka 'int &&') for 1st argument
哪一个是对的?
请考虑以下代码:
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&&>
)视为等效于转发引用的想法。
但是,似乎还有更多的事情是立即将别名模板 ID 替换为相应的类型 ID。第[14.5.7p3]段说:
但是,如果模板 id 是依赖的,则后续模板参数 替换仍适用于模板 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
—结束示例 ]
这似乎与您的示例没有太大关系,但它实际上表明在某些情况下,模板 id 的初始形式仍然被考虑在内,与替换的类型 ID 无关。我想这开启了一种可能性,即identity<T>&&
实际上实际上不能被视为转发参考。
标准中似乎未充分指定此区域。这显示了处理类似问题的未解决问题的数量,在我看来都属于同一类别:在什么情况下,在实例化时应该考虑模板 id 的初始形式,即使它应该在遇到时立即被相应的类型 id 替换。请参阅第 1980、2021 和 2025 期。甚至问题1430和1554也可以被视为处理类似的问题。
特别是,第 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 的右值引用。
所以,我认为你提出了一个非常有趣的问题。你的例子可能值得作为注释添加到第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 不合格的右值引用 模板参数,参数是左值,类型"左值引用A
"用于 类型扣除的A
地点。
这是指定转发引用如何工作的标准的一部分。P
,函数参数的类型,类型为"对identity<T>
的右值引用"。identity<T>
是模板参数的类型,但本身不是模板参数,因此转发引用扣除规则不适用。
我们还可以看看 14.5.7/2 对别名模板的看法:
当模板id 引用别名模板的专用化时,它等效于通过将其模板参数替换为别名的类型id中的模板参数而获得的关联类型模板。
所以替换的别名等同于T
的类型,但 14.8.2.1/3 读作"引用......模板参数",而不是"引用...模板参数的类型。
- 正在折叠转发引用
- C++ 何时使用常量引用而不是转发引用
- 模板模板参数和转发引用
- 常量转发引用给出错误 C2440:"正在初始化":无法从"常量标准::字符串"转换为"常量标准::字符串 &&"
- 具有右值引用,而不是使用可变参数模板转发引用
- 自定义类型转换运算符在转发引用上调用时不起作用(当对象按值传递时有效)
- 结构化绑定和转发引用是否混合良好?
- 如何声明接受转发引用并返回引用或副本的函数模板
- 间接转发引用
- 为什么调用转发引用构造函数而不是复制构造函数?
- 为什么 std::get 没有一个接受转发引用的签名
- 对 std::Optional 的转发引用构造函数的约束
- 为什么在这种情况下转发引用不起作用?
- 为什么 std::转发是转发引用所必需的
- 将一包转发引用包装成元组
- 涉及在接口中转发引用的重载
- 将转发引用作为lambda捕获传递
- 是否可以在没有类型推断的情况下实现类似转发引用的行为
- 使用确定类型转发引用行为
- 显式模板实例化和转发引用