为什么 std::max 通过 const 返回?

Why does std::max return by const&?

本文关键字:返回 const max std 为什么 通过      更新时间:2023-10-16

我想找到最大Foo并对其调用inc(),这是一种非常量方法。 当然,在寻找最大值时,我不想创建任何副本或移动,即我不想Foo foo = std::max(foo1, foo2). 我尝试编写自己的max,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;
}

您这里有 2 个问题:

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

在这种情况下:

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

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

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

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&.

可以

编写一个更复杂的版本来查找公共引用类别,但它可以以令人惊讶的方式起作用。

因此,不要被上面返回值中缺少&所愚弄:在您的用例中,上面返回一个引用。 如果传递了右值,则返回一个值。

下面是对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);
}

它也只使用<,并且更喜欢领带上的左手边。

现场示例