【问题标题】:Why does std::max return by const&?为什么 std::max 由 const 返回?
【发布时间】:2015-01-12 10:40:03
【问题描述】:

我想找到最大值Foo 并在其上调用inc(),这是一个非常量方法。当然,在找到最大值时,我不想创建任何副本或移动,即我不想要Foo foo = std::max(foo1, foo2)。我尝试编写自己的最大值,而 g++ 坚持我返回一个 const&。

#include <iostream>

class Foo
{
public:
  Foo(int x) : x_(x) { std::cout << "const" << std::endl; }
  Foo(const Foo& foo) : x_(foo.x_) { std::cout << "copy const" << std::endl; }
  Foo(Foo&& foo) : x_(foo.x_) { std::cout << "move const" << std::endl; }
  bool operator< (const Foo& foo) const { return x_ < foo.x_; }
  bool operator> (const Foo& foo) const { return x_ > foo.x_; }
  void inc() { ++x_; }
  int x_;
};

/*
 * Doesn't compile.  Must return const T& or must accept non-const T&
 *
template<typename T>
inline T& my_max(const T& f1, const T& f2)
{
  return f1 > f2 ? f1 : f2;
}
*
*/

int main()
{
  Foo foo1(6);      
  Foo foo2(7);      
  Foo& foo = std::max(foo1, foo2); //Doesn't compile.  Must be const Foo&. But then next line fails
  foo.inc();
  std::cout << foo.x_ << std::endl;
  return 0;
}

【问题讨论】:

  • 它返回一个 const 引用以避免不必要的复制,因为它早于右值引用和移动语义。如果今天创建它,它可能会有不同的定义,例如open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2199.html
  • 另一种选择是添加Foo copy() const&amp; { return *this; },这样你就可以写std::max(fooA,fooB).copy().inc();。有时复制构造函数没有名字是一件很痛苦的事情。
  • 您已经知道一个答案:您的评论说 "...或者必须接受非常量 T& ..."。您的两个变量foo1foo2 是非常量左值,您希望用于存储最大值的变量Foo&amp; foo 也是如此。那你为什么要在任何地方介绍const?只需从您的 max 中删除 const - 尽管也许您可以将其重命名为 max_lvalue_ref 以明确它的作用。
  • @AaronMcDaid 我想通过 const 通知编译器/程序员它们不会改变(想象一下,如果我们正在处理更复杂的函数)。
  • @AgrimPathak。 “他们不会改变”。你的意思是max 不会尝试更改它们?

标签: c++ reference return max constants


【解决方案1】:

这里有 2 个问题:

  1. 结果中缺少 const 限定符
  2. 返回对 const 引用参数的引用很危险

在这种情况下:

Foo& foo = std::max(Foo(6), Foo(7));

编译器会在函数调用之前为参数构造临时对象,并在函数调用之后销毁它们——所以你最终会引用垃圾。当然,如果您总是使用现有对象,它会起作用 - 但很容易忘记这些限制。

您可以从参数中删除 const ,这将解决这两个问题,这对您来说应该没问题,因为您无论如何都打算修改对象。

【讨论】:

  • std::maxconst&amp; 返回。而你似乎错过了这个问题?
  • 好吧,在写这篇文章的时候,有一个没有 const 的参数的答案,它正在做 OP 想要的,这就是为什么我没有提到它 - 如果 OP 不是,它仍然是一个很好的解决方案使用 C++11。是的,std::max 返回 const& 也很危险: const Foo& f1 = std::max(Foo(1), Foo(2)); const Foo& f2 = std::max(Foo(3), Foo(4)); f1.f 在此处的第二个运算符之后为 4。正如 OP 提到的类并且不明白为什么我们不能使用 & 而不是 const& 我想警告这个问题。
【解决方案2】:
template<typename T>
T my_max(T&& f1, T&& f2) {
  return std::forward<T>(f1 > f2 ? f1 : f2);
}

以上内容比较扎实,可以满足您的需求。它确实需要两个参数具有相同的 r/l/const ness,std::max 没有。这就是max 使用const&amp; 的原因。

可以编写一个找到共同参考类别的更复杂的版本,但它可以以令人惊讶的方式发挥作用。

所以不要被上面的返回值中缺少&amp; 所迷惑:在你的用例中,上面返回一个引用。如果传递了右值,它会返回一个值。

这是一个super_max 的尝试,如果传递相同类型的左值,则返回一个左值。如果传递了两种不同的类型或右值,则返回一个副本:

template<class A, class B>
struct max_return:std::common_type<A,B>{};
template<class A>
struct max_return<A&,A&>{
  using type=A&;
};
template<class A, class B>
using max_return_t = typename max_return<A,B>::type;

template<class T, class U>
max_return_t<T,U> super_max(T&& t, U&& u) {
  if (t < u)
    return std::forward<U>(u);
  else
    return std::forward<T>(t);
}

它也只使用&lt;,并且更喜欢打领带的左侧。

live example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-22
    • 2017-10-31
    • 2015-08-29
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多