在C++23中,由于auto()
或auto{}
,完美转发纯右值变得更容易。有了这个新工具,现在是否可以为表达式e
形成FORWARD(e)
表达式,并满足以下要求?
FORWARD(e)
与e
具有相同的类型,忽略引用限定符
1.如果decltype(e)
是左值/右值引用,则FORWARD(e)
分别是左值/x值
1.否则,FORWARD(e)
与e
具有相同的值类别
1.不可能发生额外的复制或移动
我们已经可以用std::forward
做不完美转发了:
#define FORWARD(...) ::std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)
这将保留类型并适当地转换引用。但是,纯右值将被转换为xvalue,因为std::forward
返回一个右值引用(即FORWARD(e)
是转换后的x值)。
因此:
T x = T(); // copy elision because x is initialized to prvalue
T x = FORWARD(T()); // calls move constructor
在C++中是否可以实现 true 完美转发,包括保留纯右值?
3条答案
按热度按时间biswetbf1#
你基本上希望
FORWARD(e)
是e
,除非e
碰巧命名了一个右值引用变量,在这种情况下,你希望是move(e)
。您可以简单地转换为
decltype((e))
类型(如果e
是纯右值,则将被省略),除非e
是右值引用变量,因为decltype((e))
将是左值引用。https://godbolt.org/z/WYehMxzPb
cxfofazt2#
没有
给定
E
,我们现在可以做auto(E)
,但它总是一个纯右值。左值和x值被具体化为纯右值。我们也可以做
static_cast<decltype(E)&&>(E)
†,但这始终是一个glvalue。纯右值被具体化为xvalue。P0849,这个给我们带来
auto(E)
的提案,最初也提出了decltype(auto)(E)
。这样的语法意味着:E
可以是纯右值,那么decltype(auto)(E)
将“完美转发”E
,但这是没有意义的,因为这里E
已经在做正确的事情。E
不能是纯右值(如,E
是转发引用的函数参数的名称),则decltype(auto)(E)
的含义与static_cast<decltype(E)&&>(E)
相同。后者将提供一种转发方式,而无需宏
FWD(arg)
或人们倾向于编写的C风格(Arg&&) arg
转换。然而,decltype(auto)(name)
比这两种方法都更长,也更神秘,因此它被从提案中删除。一旦你得到了一个函数参数,你就不再有纯右值了--没有办法推导出xvalue和纯右值之间的区别--所以我想不出有什么真实的的用途来完善转发,它也处理纯右值,因为你可以直接使用表达式。
†实现转发的正确方法是:
不
这两种方法的意思是一样的,但是前者避免了示例化
std::forward
和一些内部类型特征,所以编译起来更快。yqyhoc1h3#
是的,这是可能的,它不需要C++23。如果我们可以在宏中检测到表达式的值类别,我们可以选择从IILE返回此表达式。这将受到强制复制初始化的影响,并保留纯右值类别:
这些辅助函数与
std::forward
非常相似,它们可以接受左值、x值和纯右值。我们不返回xvalues和prvalues的右值引用,而是使用它们来检测值类别是什么。现在我们有了一种检测它的方法,我们可以定义实际的
FORWARD
宏:多亏了
if constexpr
和推导出的decltype(auto)
返回类型,我们的IILE有时可以返回引用,有时可以返回值,这取决于检测到的类别。这可以完美地保留类型和值类别。请参阅Compiler Explorer以获得现场演示。