0

Just when I thought I was clear about c++ rvalue references, I came across this code:

class my_class {
public:
  my_class() { }

  auto set(const std::string& s)
  {
    std::cout << "set(const std::string&) - copying unmutable lvalue" << std::endl;
    m_s = s;
  }

  auto set(std::string& s)
  {
    std::cout << "set(std::string& s) - copying mutable lvalue" << std::endl;
    m_s = s;
  }

  auto set(std::string&& s)
  {
    std::cout << "set(std::string&& s) - moving mutable rvalue" << std::endl;
    m_s = std::move(s);
  }

  auto set(const std::string&& s)
  {
    std::cout << "set(const std::string&& s) - moving unmutable rvalue" << std::endl;
    m_s = std::move(s);
  }

private:
  std::string m_s;
};

int main()
{
  my_class c;
  std::string lvalue{"lvalue"};
  const std::string const_lvalue{"const lvalue"};

  std::cout << "1. c.set(lvalue)" << std::endl;
  c.set(lvalue);

  std::cout << "2. c.set(std::move(lvalue))" << std::endl;
  c.set(std::move(lvalue));

  std::cout << "3. c.set(const_lvalue)" << std::endl;
  c.set(const_lvalue);

  std::cout << "4. c.set(std::move(const_lvalue))" << std::endl;
  c.set(std::move(const_lvalue));

  std::cout << "5. c.set(std::string{\"rvalue\"})" << std::endl;
  c.set(std::string{"rvalue"});

  // compile error
  // c.set(const std::string{"const rvalue"});

  return 0;
}

Output:
1. c.set(lvalue)
set(std::string& s) - copying mutable lvalue
2. c.set(std::move(lvalue))
set(std::string&& s) - moving mutable rvalue
3. c.set(const_lvalue)
set(const std::string&) - copying unmutable lvalue
4. c.set(std::move(const_lvalue))
set(const std::string&& s) - moving unmutable rvalue
5. c.set(std::string{"rvalue"})
set(std::string&& s) - moving mutable rvalue

I don't understand 4. Why do we bind to a const rvalue reference and how does c++ allow to invoke std::move() on this thing?

Robert Kubrick
  • 8,413
  • 13
  • 59
  • 91
  • It’s rarely useful other than for completeness and to delete a function. But it’s part of the complete set of four, so it’s probably just included for completeness. – Ben Dec 02 '22 at 00:25
  • 1
    Lot's of redundancy in the set methods but the `std::move` of a const ref defaults to a regular copy ctor since you can't move the innerds of const. – doug Dec 02 '22 at 00:43
  • If you delete `set(const std::string&& s)` overload then `c.set(std::move(const_lvalue));` will invoke `set(const std::string& s)`. Same is when you assign `m_s = std::move(s);` with `const std::string&& s` - it just invokes regular copy assignment. – dewaffled Dec 02 '22 at 00:48
  • As for when this could be useful - your classes can have `mutable` fields and you can even have something like `struct Foo { void foo() const &&; };` only accessible via `std::move(f).foo();` – dewaffled Dec 02 '22 at 00:52

0 Answers0