Niklas Niklas - 9 months ago 30
C++ Question

What effect does a const have for effect before the return type in a function?

I have the following code:

const string getConstString()
return "MyConstString";

int main()
string str1 = getConstString();
cout << str1 << endl;

What effect does the const keyword have here?


One notable effect is that you can't move from a const result.

Before C++ gained direct support for move semantics in C++11, a not uncommon advice was to make return types const in order force client code to only use the result in meaningful ways, as a value (and not as a mutable object).

After C++11 the advice is the opposite: don't make a return value const, because that prevents moving.


#include <iostream>
#include <string>
using namespace std;

auto m() -> string { return {}; }
auto c() -> const string { return {}; }

void check( string&& ) { cout << "mutable\n"; }
void check( string const&& ) { cout << "const\n"; }

auto main()
    -> int
    check( m() );
    check( c() );

This outputs


… showing that the const on the result for function c, is very much noticeable to the calling code.

With int instead of string the output is instead 2 × “mutable”, showing a subtle and surprising difference in treatment of built-in types and class types: that top-level const is ignored for a result that is of a built-in type such as int.

C++11 §3.10/4 (basic.lval/4):

Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types.

It's worth noting that this is a rule for the type of prvalue expressions, and not a rewrite rule for function declarations like the decay to pointer type of an array or function type formal argument. I.e., the type of the function is not affected. The const is there still in the type:

#include <iostream>
#include <type_traits>      // std::is_same
using namespace std;

#define STATIC_ASSERT( e ) static_assert( e, #e " <- is needed." )

auto c() -> const int{ return {}; }

auto main()
    -> int
    // Function type:
    STATIC_ASSERT(( is_same<decltype( c ), auto()->const int>::value ));
    STATIC_ASSERT(( not is_same<decltype( c ), auto()->int>::value ));

    // prvalue type:
    STATIC_ASSERT(( is_same<decltype( c() ), int>::value ));
    STATIC_ASSERT(( not is_same<decltype( c() ), int const>::value ));