171

What is the purpose of the const in this?

const Object myFunc(){
    return myObject;
}

I've just started reading Effective C++ and Item 3 advocates this and a Google search picks up similar suggestions but also counterexamples. I can't see how using const here would ever be preferable. Assuming a return by value is desirable, I don't see any reason to protect the returned value. The example given for why this might be helpful is preventing unintended bool casts of the return value. The actual problem then is that implicit bool casts should be prevented with the explicit keyword.

Using const here prevents using temporary objects without assignment. So I couldn't perform arithmetic expressions with those objects. It doesn't seem like there's ever a case that an unnamed const is useful.

What is gained by using const here and when would it be preferable?

EDIT: Change arithmetic example to any function that modifies an object that you might want to perform before an assignment.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Praxeolitic
  • 22,455
  • 16
  • 75
  • 126
  • 1
    Yes you can perform arithmetic with const objects because the arithmetic operators should be const and also return const objects – Seth Carnegie Jan 03 '12 at 17:42
  • Not sure if it deserves an answer, but another side effect of returning a const value is that it cannot be moved. Pretty much the only thing you can do to modify (the value) is to copy it into a non-const variable. I don't know if it can be used for something specific, but there you have. – alfC May 14 '22 at 07:27

4 Answers4

165

In the hypothetical situation where you could perform a potentially expensive non-const operation on an object, returning by const-value prevents you from accidentally calling this operation on a temporary. Imagine that + returned a non-const value, and you could write:

(a + b).expensive();

In the age of C++11, however, it is strongly advised to return values as non-const so that you can take full advantage of rvalue references, which only make sense on non-constant rvalues.

In summary, there is a rationale for this practice, but it is essentially obsolete.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 6
    Well, Herb Sutter [recommended returning const values](http://books.google.com/books?id=58rZvOSuheEC&lpg=PA179&ots=L_LgismXMo&dq=sutter%20return%20const%20value&pg=PA179#v=onepage&q&f=false) for non-primitive types, but I think you're right that the advice is obsolete now. – Fred Larson Jan 03 '12 at 18:03
  • 43
    @FredLarson: Yes, in a 14 year old book :-S – Kerrek SB Aug 18 '14 at 11:49
  • 1
    This answers implies that returning by const-value means const &&, but is it really true?! e.g. VS13 allows me to bind int&& var= to a function which returns const int. "The result of calling a function whose return type is not a reference is a prvalue". – Karlis Olte May 31 '15 at 21:49
  • 1
    @user64985: I don't quite understand what you're driving at. prvalues are rvalues. – Kerrek SB Jun 01 '15 at 01:56
  • 1
    I asked a question. That quote was the only related info I could find in the standard and it doesn't mention const. – Karlis Olte Jun 01 '15 at 09:45
  • But I guess they are really const&& since my compiler doesn't allow to do the same with class types. I guess compiler is just being "nice" with ints. – Karlis Olte Jun 01 '15 at 09:59
  • So @KerrekSB, Herb Sutter's advise is still valid for C++98/C++03? – stephanmg Jun 08 '20 at 07:35
  • Exactly. Watch this video https://www.youtube.com/watch?v=vLinb2fgkHk&ab_channel=InsideBloomberg . Whole point of C++11 is to make more efficient flow of object in allocators. Imagine having a class with three pointers that point to massive chunk of memory. What's smarter, to copy the entire thing, or simply to create an object that just "steals" the pointers away. That's what move does more or less. It "moves" object rather than making a direct copy. Can make your code up to 70% more efficient. const value can't be moved, so that's why this shouldn't be done anymore. – Aleksandar Demić Jun 29 '22 at 20:14
63

It's pretty pointless to return a const value from a function.

It's difficult to get it to have any effect on your code:

const int foo() {
   return 3;
}

int main() {
   int x = foo();  // copies happily
   x = 4;
}

and:

const int foo() {
   return 3;
}

int main() {
   foo() = 4;  // not valid anyway for built-in types
}

// error: lvalue required as left operand of assignment

Though you can notice if the return type is a user-defined type:

struct T {};

const T foo() {
   return T();
}

int main() {
   foo() = T();
}

// error: passing ‘const T’ as ‘this’ argument of ‘T& T::operator=(const T&)’ discards qualifiers

it's questionable whether this is of any benefit to anyone.

Returning a reference is different, but unless Object is some template parameter, you're not doing that.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Note that the second example only triggers an error for built-in types. – Xeo Jan 03 '12 at 17:47
  • 1
    Can you elaborate on the first example? It doesn't make any sense to me to say that the return value will be const, but to be able to assign it to a non-const variable. – Ivaylo Toskov Feb 09 '15 at 17:33
  • 3
    @IvayloToskov: All the elaboration you need is _in_ the example, specifically the comment that says "copies happily". Consider `const int x = 4; int y = x;` which is also perfectly fine. – Lightness Races in Orbit Feb 09 '15 at 18:47
  • @LightnessRacesinOrbit: Wow, a hole in the standard. Should be able to create an object which cannot be copied, read or updated. For small values of "x = 4". –  Jul 14 '15 at 17:49
  • @nocomprende: Why? Of what use is that? – Lightness Races in Orbit Jul 14 '15 at 17:54
  • @LightnessRacesinOrbit: the answer below describes a type that cannot be copied, only moved. My question was the same as yours. –  Jul 14 '15 at 18:13
  • @nocomprende: I can't find any area on this page in which you've asked a question. – Lightness Races in Orbit Jul 14 '15 at 18:13
  • @LightnessRacesinOrbit: It is Nicol Bolas' answer. My point was that the standards seem to allow a blizzard of completely pointless (to me) cases, which serve only as fodder for arguments. We should have stuck with a simpler language, and give the poor compilers a break. –  Jul 14 '15 at 18:18
  • @nocomprende: Your nick is amusingly apt, as I have no idea what you are talking about. – Lightness Races in Orbit Jul 14 '15 at 18:24
  • What if the method returns a const container, and the caller is using it in range-for? Without the const, it will call begin()/end(), while with the const, it will call cbegin()/cend(). In the Qt world where non-const begin is costly (might detach, because of copy-on-write), this makes a difference. The usual alternative is to put the container into a const local variable and then use that in the range-for, but that's an annoying workaround. So I'm wondering if returning a const container isn't actually a valid thing to do... (while of course the caller can make a non-const copy, I know that). – David Faure Sep 14 '19 at 12:34
  • @DavidFaure Sure, that sounds like a good reason to do it. – Lightness Races in Orbit Sep 14 '19 at 14:10
  • @LightnessRacesinOrbit actually, it turns out we're both wrong. I was talking to the head of the llvm/clang development team and he told me that returning a const value really has zero effect. I.e. in my example above, it would still call the non-const begin() and end() methods. We should think of it as a local copy, the const gets lost. He plans on submitting a paper to the C++ committee to deprecate const arguments and const return values for functions (toplevel const only, const-ref is still fine of course). – David Faure Sep 21 '19 at 12:21
  • @DavidFaure It shouldn't do that; it's non-compliant and I've never witnessed it. As for deprecating `const` arguments, that sounds silly: they're already ignored on declarations, and are _good and useful_ in definitions. – Lightness Races in Orbit Sep 21 '19 at 13:22
  • Urgh, you're right, a testcase indicates that the const does make a difference. http://www.davidfaure.fr/kde/const_retval.cpp prints "const begin" for me. I'll email him. And yes, the idea was deprecating const arguments in declarations only, not definitions. – David Faure Sep 22 '19 at 14:16
  • Re: "you can notice if the return type is a user-defined type": opening `https://www.ideone.com/z39pb` leads to "Solution not found". – pmor Mar 13 '23 at 14:46
12

It makes sure that the returned object (which is an RValue at that point) can't be modified. This makes sure the user can't do thinks like this:

myFunc() = Object(...);

That would work nicely if myFunc returned by reference, but is almost certainly a bug when returned by value (and probably won't be caught by the compiler). Of course in C++11 with its rvalues this convention doesn't make as much sense as it did earlier, since a const object can't be moved from, so this can have pretty heavy effects on performance.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Grizzly
  • 19,595
  • 4
  • 60
  • 78
  • That doesn't explain the `const`. – Nicol Bolas Jan 03 '12 at 17:48
  • @Nicol Bolas: How doesn't it explain the const? The code example while compile if the return type is `Object`, but not if it is `const Object` – Grizzly Jan 03 '12 at 17:55
  • Presumably you mean "in C++11 with its _xvalues_"? (In know that _xvalues_ and _prvalues_ are both _rvalues_, but its the _xvalues_ that are new to C++11 which are the important difference.) – CB Bailey Jan 03 '12 at 17:55
-3

It could be used as a wrapper function for returning a reference to a private constant data type. For example in a linked list you have the constants tail and head, and if you want to determine if a node is a tail or head node, then you can compare it with the value returned by that function.

Though any optimizer would most likely optimize it out anyway...

Steven Feldman
  • 833
  • 1
  • 15
  • 28
  • 4
    There's nothing to optimize, const-ness is a compile-time safety mechanism. – Paul Manta Jan 03 '12 at 17:45
  • It seems you were thinking about returning a constant _reference_ instead, so that's not relevant. And probably that should be done by returning a value copy of whatever sentinel node pointer/iterator type, no need to return any kind of reference unless it's expensive to copy. And if returning a value, there'd be no need/point to making that `const`, so full circle. – underscore_d Jul 10 '20 at 09:47