8

Let's say I have the following minimal example class:

#include <iostream>

class Foo {
public:
    Foo() = default;

    Foo(const Foo&) = default;
    Foo(Foo&&) noexcept = default;

    Foo& operator=(const Foo& rhs) {
        std::cout << "copy\n";

        return *this;
    }

    Foo& operator=(Foo&& rhs) noexcept {
        std::cout << "move\n";

        return *this;
    }

    Foo operator+(const Foo& rhs) const {
        Foo x; // with some calculation

        return x;
    }
};

int main() {
    Foo a, b, c;

    a = b + c;
}

This prints move as expected. Now according to Effective C++ Item 3, I should return const Foo from operator+ to avoid construct like a + b = c, i.e.:

// To avoid a + b = c
const Foo operator+(const Foo& rhs) const {}

Unfortunately, this suddenly starts calling copy assignment instead of move assignment operator. [I'm using gcc 4.8.4 on Ubuntu, but it is probably nothing related to compiler]

How can I ensure that a + b = c fails to compile and in the same time move assignment is called for a = b + c? Or with the introduction of move semantics, is there no way to achieve both of them in the same time?

taskinoor
  • 45,586
  • 12
  • 116
  • 142
  • 3
    That book predates move semantics, and some of it's advice is no longer applicable. – François Andrieux Feb 23 '18 at 15:50
  • @FrançoisAndrieux I know but I want to make sure that `a + b = c` doesn't compile. – taskinoor Feb 23 '18 at 15:52
  • 1
    If that's really important, maybe you could label your assignment operators with `&` ? ( [okay](https://ideone.com/bOQwwq), [not okay](https://ideone.com/EZBL5j) ) Not sure if that's a wise thing to do in general, though – Caninonos Feb 23 '18 at 15:53
  • You could return a non-assignable proxy object, though you have to wonder if it's worth it. – François Andrieux Feb 23 '18 at 15:53
  • 2
    Here, regarding my earlier comment [Should I use lvalue reference qualifiers for assignment operators? (stackoverflow link)](https://stackoverflow.com/questions/12759340/should-i-use-lvalue-reference-qualifiers-for-assignment-operators) – Caninonos Feb 23 '18 at 15:54
  • @FrançoisAndrieux proxy seems overkill for my purpose. – taskinoor Feb 23 '18 at 16:02
  • @Caninonos I wan't aware about lvalue reference qualifier. That looks promising. – taskinoor Feb 23 '18 at 16:03
  • 2
    I have never seen anyone write `a + b = c;` by mistake (instead of `a = b + c;`) and then not discover this during testing. Are you *sure* this is something you have to worry about? – Bo Persson Feb 23 '18 at 16:57
  • @BoPersson I'm not much worried about writing `a + b = c` by mistake. Instead I'm more interested to learn whether is it still possible to enforce it easily like the way it was before by simply returning `const`. – taskinoor Feb 23 '18 at 17:18
  • 1
    @BoPerson On the other hand, by mistake `a + b = c` could be written instead of `a + b == c`. – Oliv Feb 23 '18 at 18:20
  • 1
    Turbo C++ from 1990 introduced the warning "possible incorrect assignment". You guys can stop worrying about = vs == now. That is, given that you have a compiler which isn't worse than TC++ from 1990. – Lundin Feb 26 '18 at 16:13

1 Answers1

2

I have ended up using lvalue reference qualifier as pointed by Caninonos in comment and by max66 in now deleted answer (but 10k users can see it).

Foo& operator=(const Foo& rhs) & {}
Foo& operator=(Foo&& rhs) & noexcept {}

It is simple to implement and it provides a better interface design since assignment to anything other that lvalue doesn't sound meaningful and a possible source of bug.

However, it should be noted that the possibility of writing a + b = c by mistake is very low. Also compiler generated assignment operators are not lvalue reference qualified and we can write a + b = c with standard types, e.g. with std::string or with std::complex.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
taskinoor
  • 45,586
  • 12
  • 116
  • 142