5

为什么std::forward需要重载出& 和 && 两个版本?

 3 years ago
source link: https://www.zhihu.com/question/451601552/answer/1822164295
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

为什么std::forward需要重载出& 和 && 两个版本?

这是type_traits中对std::forward的实现: // FUNCTION TEMPLATE forward template<cl…
8
1,384
登录一下,更多精彩内容等你发现
贡献精彩回答,参与评论互动
C++程序猿, 公众号:高级开发者

std::forward 的第一个版本已经有人回答了,相信答主也能理解,其能应对几乎所有场景。我来回答一下为何还需要第二个版本的 std::forward

考虑如下代码。

template<typename T>
auto createObj() {
    return std::decay_t<T>{};
}

template<typename T>
void f(T&& x) {
    Func(std::forward<T>(createObj<T>()));
}

如果没有第二个版本的std::forward,代码编译不过。第二个版本其实处理的是当实参为右值的情况。

Forwards rvalues as rvalues and prohibits forwarding of rvalues as lvalues

This overload makes it possible to forward a result of an expression (such as function call), which may be rvalue or lvalue, as the original value category of a forwarding reference argument.

https://en.cppreference.com/w/cpp/utility/forward

继续浏览内容
v2-88158afcff1e7f4b8b00a1ba81171b61_720w.png
发现更大的世界
v2-a448b133c0201b59631ccfa93cb650f3_1440w.png
Chrome
老程序员,C++顾问、培训师

你的代码在右值的情况下实际是错的。此时,T 被推导为 A,所以代码成了下面这个样子:

A&& MyForward(A t)
{
    return static_cast<A&&>(t);
}

你会返回一个即将消失的非引用参数的引用……事实上,在打开常见的告警选项后(如 -Wall -Wextra),像 GCC 和 Clang 这样的编译器是可以发现这个错误的。

那是不是在参数上加上 && 就行了呢?也不行,同样会出错,因为右值引用是个左值(确实有点让人发疯,然而这是现实),匹配不了 A&&。这时,你就看到标准库里的实现方法的作用了:

template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept
{	
    // forward an lvalue as either an lvalue or an rvalue
    return (static_cast<_Ty&&>(_Arg));
}

这个重载在指定 _Ty 为 A 时(当你的 PerfectForward 函数推导出类型为 A),能够接受一个匹配左值引用的参数,并重新把它强制转换成 A&&……这就完成了 forward 的任务。

继续浏览内容
v2-88158afcff1e7f4b8b00a1ba81171b61_720w.png
发现更大的世界
v2-a448b133c0201b59631ccfa93cb650f3_1440w.png
Chrome
兴趣使然的学生
创作声明: 内容包含医疗建议
template<typename T>
T&& MyForward(T t)
{
	return static_cast<T&&>(t);
}

这里的函数形参不是一个“万能引用”或“转发引用”[1]

这样MyForward在接受了一个xvalue的情况下(测试代码中第二次调用PerfectForward)T会被推导为无引用的版本

template<>
A&& MyForward(A t)
{
	return static_cast<A&&>(t);
}

显然这里的行为将会是调用A的复制构造函数初始化参数t,这就完全没法称作“转发”了

为什么需要重载?直接使用引用折叠不是就已经达到目的了吗?

是希望T推导为A&&吗?但模板类型参数永远不会被推导为右值引用类型[2]

继续浏览内容
v2-88158afcff1e7f4b8b00a1ba81171b61_720w.png
发现更大的世界
v2-a448b133c0201b59631ccfa93cb650f3_1440w.png
Chrome

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK