简体   繁体   中英

What's a use case for overloading member functions on reference qualifiers?

C++11 makes it possible to overload member functions based on reference qualifiers:

class Foo {
public:
  void f() &;   // for when *this is an lvalue
  void f() &&;  // for when *this is an rvalue
};

Foo obj;
obj.f();               // calls lvalue overload
std::move(obj).f();    // calls rvalue overload

I understand how this works, but what is a use case for it?

I see that N2819 proposed limiting most assignment operators in the standard library to lvalue targets (ie, adding " & " reference qualifiers to assignment operators), butthis was rejected . So that was a potential use case where the committee decided not to go with it. So, again, what is a reasonable use case?

In a class that provides reference-getters, ref-qualifier overloading can activate move semantics when extracting from an rvalue. Eg:

class some_class {
  huge_heavy_class hhc;
public:
  huge_heavy_class& get() & {
    return hhc;
  }
  huge_heavy_class const& get() const& {
    return hhc;
  }
  huge_heavy_class&& get() && {
    return std::move(hhc);
  }
};

some_class factory();
auto hhc = factory().get();

This does seem like a lot of effort to invest only to have the shorter syntax

auto hhc = factory().get();

have the same effect as

auto hhc = std::move(factory().get());

EDIT: I found the original proposal paper , it provides three motivating examples:

  1. Constraining operator = to lvalues (TemplateRex's answer)
  2. Enabling move for members (basically this answer)
  3. Constraining operator & to lvalues. I suppose this is sensible to ensure that the "pointee" is more likely to be alive when the "pointer" is eventually dereferenced:
struct S {
  T operator &() &;
};

int main() {
  S foo;
  auto p1 = &foo;  // Ok
  auto p2 = &S();  // Error
}

Can't say I've ever personally used an operator& overload.

One use case is to prohibit assignment to temporaries

 // can only be used with lvalues
 T& operator*=(T const& other) & { /* ... */ return *this; } 

 // not possible to do (a * b) = c;
 T operator*(T const& lhs, T const& rhs) { return lhs *= rhs; }

whereas not using the reference qualifier would leave you the choice between two bads

       T operator*(T const& lhs, T const& rhs); // can be used on rvalues
 const T operator*(T const& lhs, T const& rhs); // inhibits move semantics

The first choice allows move semantics, but acts differently on user-defined types than on builtins (doesn't do as the ints do). The second choice would stop the assigment but eliminate move semantics (possible performance hit for eg matrix multiplication).

The links by @dyp in the comments also provide an extended discussion on using the other ( && ) overload, which can be useful if you want to assign to (either lvalue or rvalue) references.

如果 f() 需要一个 Foo temp 是 this 的副本并已修改,您可以修改 temp this而您不能否则

On the one hand you can use them to prevent functions that are semantically nonsensical to call on temporaries from being called, such as operator= or functions that mutate internal state and return void , by adding & as a reference qualifier.

On the other hand you can use it for optimizations such as moving a member out of the object as a return value when you have an rvalue reference, for example a function getName could return either a std::string const& or std::string&& depending on the reference qualifier.

Another use case might be operators and functions that return a reference to the original object such as Foo& operator+=(Foo&) which could be specialized to return an rvalue reference instead, making the result movable, which would again be an optimization.

TL;DR: Use it to prevent incorrect usage of a function or for optimization.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM