如何编写正向函数模板?

问题描述

我想了解更多 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))

是的,这很危险,因为 Tint& 并且返回值 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;