-1

I just discovered this is valid C++:

struct S { 
  int f() &;         // !
  int g() const &;   // !!
}; 

int main() {
    S s;
    s.f();
    s.g();
}

IIUC, this is passed to f by reference and to g be passed by const-reference.

How was this useful to anyone?

Clifford
  • 88,407
  • 13
  • 85
  • 165
Ofek Shilon
  • 14,734
  • 5
  • 67
  • 101
  • @AndyG Thanks! How can I control which overload is called? And can you give an example why would I want that? – Ofek Shilon Apr 12 '22 at 15:07
  • https://en.cppreference.com/w/cpp/language/function – 463035818_is_not_an_ai Apr 12 '22 at 15:08
  • In addition to duplicate, note that `const` and `&` are completely separate in this case. `int g() const` would get you the same result (`this` would be `const S*`). However, you now cannot do `S{}.f();` or `S{}.g();`, both will be compiler errors. – Yksisarvinen Apr 12 '22 at 15:15
  • I could see a hypothetical situation where I have some particularly destructive functions that should only be called on an r-value reference, and I want to prevent the user from calling them on an l-value ref. – JohnFilleau Apr 12 '22 at 15:17
  • The marked duplicate is about overloaded functions with reference qualifiers - and therefore not a duplicate - it is a (perhaps subtly) different use case. – Clifford Apr 12 '22 at 16:59

1 Answers1

2

They are useful for both providing safety and optimizations.

For member functions returning a pointer to something they own (either directly or via view types like std::string_view or std::span), disabling the rvalue overloads can prevent errors:

struct foo {
    int* take_ptr_bad() { return &x; }
    int* take_ptr() & { return &x; }
    int x;
};

foo get_foo();

void bar() {
    auto ptr = get_foo().take_ptr_bad();
    // ptr is dangling
    auto ptr = get_foo().take_ptr();
    // does not compile
}

The other is to provide some optimizations. For instance, you might overload a getter function to return an rvalue reference if this is an rvalue to prevent unnecessary copies:

struct foo {
    const std::string& get_str() const & {
        return s;
    }
    
    std::string&& get_str() && {
        return std::move(s);
    }
    
    std::string s;
};

void sink(std::string);

foo get_foo();

void bar() {
    sink(get_foo().get_str()); 
    // moves the string only if the r-value overload is provided.
    // otherwise a copy has to be made, even though the foo object
    // and transitively the string is temporary.
}

These are how I use the feature, and I'm sure there are more use cases.

Fatih BAKIR
  • 4,569
  • 1
  • 21
  • 27