返回一个转发引用

3
我希望构建一个最大化函数,将较大的值转发到结果中,并保持引用类型(rvalue或lvalue)。
#include <utility>

template<typename T>
constexpr T&& mmax(T&& left, T&& right) {
    return left > right ? std::forward<T>(left) : std::forward<T>(right);
}

int main() {
    mmax(1, 2);
}

但是,这个给我

max.cc: In instantiation of 'constexpr T&& mmax(T&&, T&&) [with T = int]':
max.cc:9:14:   required from here
max.cc:5:72: warning: returning reference to temporary [-Wreturn-local-addr]
     return left > right ? std::forward<T>(left) : std::forward<T>(right);

为什么会这样?我正在使用带有-std=c++11的GCC 4.8.2。
编辑:clang++不会发生这种情况。

你使用的编译器是什么?我使用的是Apple LLVM 5.0,没有收到这个警告。 - Kevin MOLCARD
@KevinMOLCARD,GCC 4.8.2,我更新了问题。 - Fabian Knorr
@KerrekSB 我希望所有参数都是同一种引用类型,这样返回类型就不会有歧义。为什么多次使用相同的通用引用会有问题? - Fabian Knorr
@trion:这有点违背了它的初衷:通用引用应该始终是一个引用,并根据提供的参数选择左值或右值类型。这种逻辑不适用于多个参数。如果所有参与的参数都是同一种类型,那么你可能会“得到你想要的”,否则你可能会得到一个推导失败,或者你可能会将一个右值静默地绑定到一个const左值引用上... - Kerrek SB
1
@trion:推导一个参数并强制另一个具有相同类型的标准方法是使第二个类型不可推导,例如:template <typename T> void foo(T && a1, typename std::common_type<T>::type a2) 现在 a1 通过通用引用绑定,而 a2 必须能够匹配它。 - Kerrek SB
显示剩余7条评论
1个回答

4
您的原始代码应该可以工作,在C++ 11 5.16/4中的条件运算符规则下,因为:

如果第二个和第三个操作数是相同值类别的glvalue并且具有相同的类型,则结果是该类型和值类别

表达式forward<T>(left)forward<T>(right)都是lvalue或xvalue,因此它们始终是glvalue。它们都是类型T,因此该规则适用,并且条件表达式的类型应该是相同的类型和值类别。
但是,为了解决潜在的编译器错误问题,您可以使用以下表达式,避免计算条件表达式的类型和值类别(这似乎是错误发生的地方):
return std::forward<T>(left > right ? left : right);

1
上面的危险在于,如果进行了转换,现在你会悄悄地返回一个对临时对象的引用。如果您使用T&&并返回T,它还可以启用引用生命周期延长。 - Yakk - Adam Nevraumont
1
@Yakk: 嗯,很难说一个不符合规范的编译器会做什么。我们知道leftright都是引用,所以不应该有任何临时变量,但谁知道呢? - Kerrek SB
@Kerrek: 为什么不能有临时对象?只有非const左值引用才无法绑定到临时对象,而这个函数接受const左值引用和右值引用。 - Ben Voigt
@BenVoigt:但是这些不是函数创建的临时变量。它们是调用者的责任。 - Kerrek SB
如果exp1或exp2是临时变量,那么请检查auto&& x=mmax(exp1,exp2);。你很少想要返回一个T&& - 返回一个T会更好。 - Yakk - Adam Nevraumont
@Yakk:是的,我知道你可以滥用这个功能,但OP似乎已经意识到了这一点,因为她提出了最初的设计。 - Kerrek SB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接