返回转发参考参数 - 最佳实践

时间:2016-03-17 07:02:06

标签: c++ c++11 c++14 move-semantics rvalue-reference

在以下场景中

template <class T>
? f(T&& a, T&& b)
{
    return a > b ? a : b; 
}

最佳回报类型是什么?到目前为止我的想法是:

  1. 返回r值refs,完美转发函数参数:

    template <class T>
    decltype(auto) f(T&& a, T&& b)
    {
        return a > b ? forward<T>(a) : forward<T>(b); 
    }
    
  2. 移动构造返回值:

    template <class T>
    auto f(T&& a, T&& b)
    {
        return a > b ? forward<T>(a) : forward<T>(b); 
    }
    
  3. 尝试寻找启用(N)RVO的方法(即使我认为因为我想使用那些不可能的函数参数)

  4. 这些解决方案有问题吗?还有更好的吗?什么是最佳做法?

2 个答案:

答案 0 :(得分:5)

对于选项1,您需要使用两个模板参数,以便在两个参数具有不同的值类别时启用转发。它应该是

template <typename T, typename U>
decltype(auto) f(T&& a, U&& b)
{
    return a > b ? std::forward<T>(a) : std::forward<U>(b);
}

实际返回的内容非常棘手。首先,您需要了解ab的值类别,以及推导出TU的值。然后,无论您选择哪种配对,都要经历三元运算符的非常复杂的规则。最后,它的输出通过decltype规则来为您提供函数的实际返回类型。

我确实坐了下来,一次又一次地工作,with a little help from SO。在我的记忆中,它是这样的:当且仅当ab是兼容类型的左值引用时,结果才是可变的左值引用;如果ab之一是const左值引用而另一个是任何类型的引用,则结果将是const左值引用;否则f将返回一个新值。

换句话说,它对任何给定的参数都做了正确的事情 - 可能是因为有人坐下来准确地编写规则,以便 在所有情况下做正确的事情。< / p>

选项2与选项1完全相同,只是decltype规则没有进入,并且该函数将始终返回一个新值 - plain auto从不推断参考。

我想不出一个可以在RVO方面工作的选项3,因为你说你正在使用这些参数。

总而言之,我认为(1)是您正在寻找的答案。

答案 1 :(得分:2)

这取决于您的意图和T的移动意识。每个案例都有效,但行为有差异;这个:

template <class T>
decltype(auto) f(T&& a, T&& b)
{
    return a > b ? forward<T>(a) : forward<T>(b); 
}

将完美地转发左值或右值参考值,从而完全没有临时值。如果你在那里放一个inline(或者编译器给你放的话)就好像有参数一样。对于“第一视觉直觉”,由于“引用”死堆栈对象(对于右值情况),它不会产生任何运行时错误,因为我们转发的任何临时来自我们上方的呼叫者(或低于我们,取决于你如何在函数调用期间考虑堆栈)。另一方面,这(在右边的情况下):

template <class T>
auto f(T&& a, T&& b)
{
    return a > b ? forward<T>(a) : forward<T>(b); 
}

将移动 - 构造一个值(auto永远不会推导出&&&),如果T不能移动构造,那么您将调用一个副本构造函数(如果T是左值引用,则始终生成副本)。如果你在一个表达式中使用f,你毕竟会生成一个xvalue(这让我想知道编译器是否可以使用初始rvalue,但我不打赌它)。

长话短说,如果使用f的代码以一种处理rvalue和lvalue引用的方式构建,我会选择第一个选择,毕竟逻辑std::forward是如果你有左值参考,你就不会产生任何副本。