自 C++11 年以来对 std::forward 实施的理解
Understanding of the implementation of std::forward since C++11
(constexpr
和noexcept
被排除在外,因为它们似乎与理解std::forward
的行为无关。
根据我对斯科特·迈耶斯(Scott Meyers)的"有效的现代C++"的理解, C++14 中std::move
的示例实现如下
template<typename T>
decltype(auto) move(T&& param) {
return static_cast<remove_reference_t<T>&&>(param);
}
鉴于转发(或"通用")引用是什么的解释,我认为这个实现对我来说很清楚:
参数param
的类型为T&&
,即 右值引用或左值- 引用(无论参数的类型是什么),这取决于参数在调用者中是右值还是左值;换句话说,
param
可以绑定到右值和左值(即任何东西);这是有意的,因为move
应该将任何东西转换为右值。 decltype(auto)
只是根据实际return
语句表示返回类型的简洁方法。- 返回的对象是相同的对象
param
,一旦它的推导引用被剥离(推导的引用性),就强制转换为右值引用(&&
)到任何类型T
(推导是在T&&
上完成的,而不是在⋯<T>&&
上完成的)。
简而言之,我对在实现move
中使用转发/通用引用的理解如下:
- 转发/通用引用
T&&
用于参数,因为它旨在绑定到任何内容; - 返回类型是右值引用,因为
move
旨在将任何内容转换为右值。
很高兴知道到目前为止我的理解是否正确。
另一方面,C++14 中std::forward
的示例实现如下
template<typename T>
T&& forward(remove_reference_t<T>& param) {
return static_cast<T&&>(param);
}
我的理解如下:
T&&
,返回类型,必须是转发/通用引用,因为我们希望是对传递给forward
通过右值引用或左值引用返回,因此这里的返回类型进行类型推断(不像move
发生的情况,其中类型推断发生在参数端)forward
的任何模板类型参数的右值引用;- 由于
T
编码实际参数的左值/右值,该参数绑定了作为参数传递给forward
的调用方参数,T
本身可以导致actual_type&
或actual_type
,因此T&&
可以是左值引用或右值引用。 param
的类型是T
类型的左值引用,一旦其推导的引用性被剥离。 实际上,在std::forward
类型推断是故意禁用的,需要显式传递模板类型参数。
我的疑问如下。
forward
的两个实例(实际上,每个调用它的类型两个)仅返回类型不同(传递右值时的右值引用,传递左值时的左值引用),因为在这两种情况下,param
的类型都是对非const
无引用T
的左值引用。返回类型不计入重载解析吗?(也许我在这里不恰当地使用了"重载"。- 由于
param
的类型是非const
无引用T
的左值引用,并且由于左值引用必须const
才能绑定到右值,param
如何绑定到右值?
作为一个附带问题:
decltype(auto)
可以用于返回类型,就像它用于move
一样吗?
forward
本质上是一种在完美转发中保存价值类别的机器。
考虑一个简单的函数,它尝试透明地调用f
函数,同时尊重值类别。
template <class T>
decltype(auto) g(T&& arg)
{
return f(arg);
}
在这里,问题是表达式arg
始终是左值,无论arg
是否为右值引用类型。 这就是forward
派上用场的地方:
template <class T>
decltype(auto) g(T&& arg)
{
return f(forward<T>(arg));
}
考虑std::forward
的参考实现:
template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept
{
return static_cast<T&&>(t);
}
template <class T>
constexpr T&& forward(remove_reference_t<T>&& t) noexcept
{
static_assert(!std::is_lvalue_reference_v<T>);
return static_cast<T&&>(t);
}
(您可以在此处使用decltype(auto)
,因为推导的类型将始终T&&
。
在以下所有情况下,调用第一个重载是因为表达式arg
表示变量,因此是左值:
如果使用非常量左值调用
g
,则T
被推导为非常量左值引用类型。T&&
与T
相同,forward<T>(arg)
是一个非常量的左值表达式。 因此,f
是使用非常量左值表达式调用的。如果使用 const 左值调用
g
,则T
被推导为 const lvalue 引用类型。T&&
与T
相同,forward<T>(arg)
是一个常量左值表达式。 因此,f
是使用 const 左值表达式调用的。如果使用右值调用
g
,则T
推导为非引用类型。T&&
是右值引用类型,forward<T>(arg)
是右值表达式。 因此,f
是使用 rvalue 表达式调用的。
在所有情况下,都尊重价值类别。
第二个重载不用于正常完美转发。 请参阅 std::forward() 的右值引用重载的目的是什么?为其使用。
- 完美前进使用 std::forward vs RefRefCast
- 你如何理解"std: :forward is just syntactic sugar"?这是真的吗?
- "std::forward"和"std::move"真的不生成代码吗?
- 在C++中使用 std::forward 的多个参数
- 如何使用 std::forward 精确地评估参数包的扩展?
- 普通的右值引用和 std::forward 返回的引用有什么区别?
- 怎么可能写 f( *this, std::forward<Args>(args)... ) 而 f 只用 F f 声明;
- 如何通过通用引用或std::forward将这三个c++模板函数合并为一个
- 自 C++11 年以来对 std::forward 实施的理解
- 当将参数包传递给另一个可变参数模板函数时,我是否必须使用 std::forward
- 为什么要在概念中使用std::forward
- 寻求对std::forward的澄清
- 为什么 std::forward 将左值和右值转换为右值引用?
- std::forward() 的右值引用重载的目的是什么?
- std::forward 如何推断出"_Ty"的类型?
- 没有匹配函数调用 std::forward(const std::string &) 与可变参数
- C++ decltype(auto) or decltype(std::<T>forward(value))?
- std::function operator() 和 std::forward 中发生了什么?
- How to std::forward( *this )
- 在哪些情况下,从 std::forward 分配比从 std::move 分配更可取?为什么