问题描述
我想了解更多 std::forward
是如何工作的,以及显式模板参数和模板参数推导如何与转发引用一起工作,我试过这个例子:
template <typename T>
T&& fwd_(typename std::remove_reference<T>::type& arg){
std::cout << "fwd_(type&)\n";
return static_cast<T&&>(arg);
}
template <typename T>
T&& fwd_(typename std::remove_reference<T>::type&& arg){
std::cout << "fwd_(type&&)\n";
++arg;
return static_cast<T&&>(arg);
}
int main(){
int&& l = fwd_<int>(5);
int&& m = fwd_<int&&>(7);
int& j = fwd_<int&>(m);
int& k = fwd_<int&>(7); // UB?
std::cout << k << std::endl;
std::cout << '\n';
}
- 我不知道
k
发生了什么?为什么我可以将 r 值作为 l 值传递? (fwd_<int&>(7)
)?
我确信左值引用绑定到函数模板参数;不是传递的参数。
我也确信该表达式会产生未定义的行为,因为当 fwd_
返回时该参数会被销毁。
-
我已经尝试过并更改了优化级别,因此由于 UB,我得到了不同的值。
-
我的想法正确吗?如果是这样,我怎么能写出一个干净且正确的版本来模仿图书馆
std::forward
?谢谢!
解决方法
我不知道k怎么了?为什么我可以将 r 值作为 l 值传递? (fwd_<int&>(7))
?
是的,这很危险,因为 T
是 int&
并且返回值 T&&
,作为 reference collapsing 的结果,我们得到 int& &&
→ {{1} },即您最终返回对临时对象的引用,该引用不会延长生命周期,从而在完整表达式(int&
)结束时销毁临时对象后导致 UB。
出于这个原因,C++ 标准明确 excludes 来自 ;
的这种用法:
std::forward
template <class T> constexpr T&& forward(remove_reference_t<T>& t) noexcept;
. . .
3 备注:如果第二种形式是用左值实例化的 引用类型,程序格式错误。
例如,libstdc++ 有一个 assertion 在这种情况下会提前失败:
template <class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;