25

Possible Duplicate:
Should I return const objects?
(The original title of that question was: int foo() or const int foo()? explaining why I missed it.)


Effective C++, Item 3: Use const whenever possible. In particular, returning const objects is promoted to avoid unintended assignment like if (a*b = c) {. I find it a little paranoid, nevertheless I have been following this advice.

It seems to me that returning const objects can degrade performance in C++11.

#include <iostream>
using namespace std;

class C {
public:
    C() : v(nullptr) { }

    C& operator=(const C& other) {
        cout << "copy" << endl;
        // copy contents of v[]
        return *this;
    }

    C& operator=(C&& other) {
        cout << "move" << endl;
        v = other.v, other.v = nullptr;
        return *this;
    }

private:
    int* v;
};

const C const_is_returned() { return C(); }

C nonconst_is_returned() { return C(); }

int main(int argc, char* argv[]) {
    C c;
    c = const_is_returned();
    c = nonconst_is_returned();
    return 0;
}

This prints:

copy
move

Do I implement the move assignment correctly? Or I simply shouldn't return const objects anymore in C++11?

Community
  • 1
  • 1
Ali
  • 51,545
  • 25
  • 157
  • 246
  • 1
    @jogojapan Yes. It is a shame that question has such a poorly chosen title which pretty much explains why I missed it. The answers to my question are the ones from [Kerrek SB](http://stackoverflow.com/a/12051169/341970) and [sbi](http://stackoverflow.com/a/12051516/341970). Thanks for pointing this out! – Ali Oct 27 '12 at 12:05
  • 3
    This is not an exact duplicate – Industrial-antidepressant Oct 28 '12 at 01:47

3 Answers3

25

Returning const objects is a workaround that might cause other problems. Since C++11, there is a better solution for the assignment issue: Reference Qualifiers for member functions. I try to explain it with some code:

int foo(); // function declaration
foo() = 42; // ERROR

The assignment in the second line results in a compile-time error for the builtin type int in both C and C++. Same for other builtin types. That's because the assignment operator for builtin types requires a non-const lvalue-reference on the left hand side. To put it in code, the assignment operator might look as follows (invalid code):

int& operator=(int& lhs, const int& rhs);

It was always possible in C++ to restrict parameters to lvalue references. However, that wasn't possible until C++11 for the implicit first parameter of member functions (*this).

That changed with C++11: Similar to const qualifiers for member functions, there are now reference qualifiers for member functions. The following code shows the usage on the copy and move operators (note the & after the parameter list):

struct Int
{
    Int(const Int& rhs) = default;
    Int(Int&& rhs) noexcept = default;
    ~Int() noexcept = default;
    auto operator=(const Int& rhs) & -> Int& = default;
    auto operator=(Int&& rhs) & noexcept -> Int& = default;
};

With this class declaration, the assignment expression in the following code fragment is invalid, whereas assigning to a local variable works - as it was in the first example.

Int bar();
Int baz();
bar() = baz(); // ERROR: no viable overloaded '='

So there is no need to return const objects. You can restrict the assigment operators to lvalue references, so that everything else still works as expected - in particular move operations.

See also:

nosid
  • 45,370
  • 13
  • 104
  • 134
  • @nosid Thanks. Could you please expand on how your second code snippet prevents assignment to lvalues? I am just learning the C++11 features and I am not familiar with what you are writing. – Ali Oct 27 '12 at 12:09
  • @Ali it forbids assignment to rvalues because it declares an `operator=` that can only be used on lvalues (notice the `&` after the parameter list: that's what it means). – R. Martinho Fernandes Oct 27 '12 at 12:15
  • 1
    @R.MartinhoFernandes Yes, I noticed the `&` but it is another C++11 feature that I am not familiar with. Chances are, some of the future googlers won't be either. How is it called? In my opinion, it should be explained in the answer. It looks like a good recipe, I hope nosid will update his answer. – Ali Oct 27 '12 at 12:17
  • 1
    @Ali it is sometimes referred to as "rvalue *this" (because there's also a `&&` form that restricts usage to rvalues), but I and others find that is a terrible, terrible name. The standard calls it function ref-qualifiers. See this question for more info: http://stackoverflow.com/questions/8610571/what-is-rvalue-reference-for-this/8610728#8610728 – R. Martinho Fernandes Oct 27 '12 at 12:21
  • Nice usage of "function ref-qualifiers"! Thank you for the insightful post! – Ralph Tandetzky Jul 08 '13 at 10:02
24

Returning a const object by value arguably was never a very good idea, even before C++11. The only effect it has is that it prevents the caller from calling non-const functions on the returned object – but that is not very relevant given that the caller received a copy of the object anyway.

While it is true that being returned a constant object prevents the caller from using it wrongly (e.g. mistakenly making an assignment instead of a comparison), it shouldn't be the responsibility of a function to decide how the caller can use the object returned (unless the returned object is a reference or pointer to structures the function owns). The function implementer cannot possibly know whether the object returned will be used in a comparison or for something else.

You are also right that in C++11 the problem is even graver, as returning a const effectively prevents move operations. (It does not prevent copy/move elision, though.)

Of course it is equally important to point out that const is still extremely useful (and using it no sign of paranoia) when the function returns a reference or a pointer.

jogojapan
  • 63,098
  • 9
  • 87
  • 125
3

The reason that your call to const_is_returned triggers copy constructor rather than move constructor is the fact that move must modify the object thus it can't be used on const objects. I tend to say that using const isn't advised in any case and should be subjected to a programmer judgement, otherwise you get the things you demonstrated. Good question.

SomeWittyUsername
  • 17,203
  • 3
  • 34
  • 78