What is the difference between "std::string const &s" and "const std::string &s"?

19,042

Solution 1

std::string const & is equivalent to const std::string &.

const std::string & is the style adopted in Stroustrup's The C++ Programming Language and probably is "the traditional style".

std::string const & can be more consistent than the alternative:

the const-on-the-right style always puts the const on the right of what it constifies, whereas the other style sometimes puts the const on the left and sometimes on the right.

With the const-on-the-right style, a local variable that is const is defined with the const on the right: int const a = 42;. Similarly a static variable that is const is defined as static double const x = 3.14;. Basically every const ends up on the right of the thing it constifies, including the const that is required to be on the right: with a const member function.

(see What do “X const& x” and “X const* p” mean? for further details).

If you decide to use const-on-the-right style, make sure to don't mis-type std::string const &s as the nonsensical std::string & const s:

The above declaration means: "s is a const reference to a std::string". It's redundant since references are always const (you can never reset a reference to make it refer to a different object).

Solution 2

Technically it is the same and does not make any difference. But in some debuggers (lldb for sure) you will see std::string const& even if you have written as const std::string&.

Solution 3

As noted, they're the same type. One reason to like the const on the right is how it plays with templates. Most people process function templates by just substitution. For example:

template <class T> void foo(const T& arg);

int* p;
foo(p);

What is the type of arg? You want to say const int*&, which is to say, a reference to a pointer to const int, but that is wrong. Text substitution has failed you. If you has written it instead like

template <class T> void foo(T const& arg);

Then simple text substitution yields the correct type: int* const&. That is, a reference to const pointer to int.

Solution 4

Just to prove other people assertations (I understand that rtti doesn't take constness or voltilness into the .name() output )

this test program:

 #include <string>
 #include <iostream>

 using std::string;
 using std::cout;
 using std::cin;

  using std::endl;

  int main()
  {
    std::string t = "BLABLA" ;
    std::string t1 = "BLABLA" ;
    std::string const &s = t;
    const std::string &d = t1;


if (typeid(d) == typeid(s)) 
    cout << "d and s are the same type according to the rtti" << endl;

else
    cout << "d and s are the NOT the same type according to the rtti" <<  endl;
    // here the raw output is exactly the same for both
    cout << typeid(d).raw_name() << endl << typeid(s).raw_name() << endl; 

    cin >> t;
    return 0;
}

both for gcc (4.9.2) (with proper modifications) msdn (vs2010) returns "same type" for both.

I purpose this "answer" as a contributation and not as an "answer".

EDIT: Reading item 4 in "Effective Modern C++" of Scott-Meyers shares some really interesting hidden information about the example I wrote. In short, typeid doesn't yield reliable results since we are passing variable names by value. When we are passing a method a variable by value, the deduced type loses its constness and volatilness and referenceness (for the love of god will be shortened to cvr) . That's why the above example doesn't prove if there is difference between the two types.

The same Item states that Boost project hosts a library called TypeIndex, which preserves cvr properties.

Solution 5

As Barry noted in his answer the old C syntax (and the C++ extension of that syntax) doesn't support the conceptual view of text substitution, as in mathematics.

Using the C syntax directly it's therefore generally a good idea to write T const, not const T, even though when T is a simple type these type expressions are equivalent. Writing T const also avoids inconsistency in a multi-level pointer declaration like char const* const p;, where the last const can't be moved. Which exemplifies that the equivalence is only for the base type at the start of a declaration.

Some months ago I started an experiment writing const T, and using that notation consistently. Because that's now possible after C++11, without using macros. To support the consistency I use named type builders like

template< class Some_type >
using Ptr_ = Some_type*;

so that instead of the read-from-right-to-left

char const* const p = something;

I can and do write the ordinary reading direction

const Ptr_<const char> p = something;

It's little bit more verbose, but I now, with some experience using it, think it's worth it (I was not sure about that, it was an experiment).

The main drawback is that while this notation supports type deduction for (function template) function arguments just like direct use of C syntax, it does not support type deduction for auto declarations. Happily, since an initializer can easily be made const, about the only problematic case is reference to array. My pragmatic solution so far has been to use the C (or rather, C++) syntax directly in such cases, but I think this represents a shortcoming of or hole in the language, possibly with some general solution that would make things much easier also in other contexts.

For my current type builders like Ptr_, see the cppx/core_language_support/type_builders.hpp file at Github. They include e.g. In_ for function arguments. And yes, I've found that also worth it, in clarity, in spite of some verbosity, because it makes the intent very clear. However, the cppx stuff is experimental and subject to change. The specific file linked to here may even be moved, but it will be thereabouts. :)

Share:
19,042

Related videos on Youtube

Thomas Good
Author by

Thomas Good

Updated on July 01, 2022

Comments

  • Thomas Good
    Thomas Good almost 2 years

    I was looking for examples on how to do something and saw this two variants:

    std::string const &s;
    const std::string &s;
    

    in different snippets.

    thx for your answer :)

    • Daniel Jour
      Daniel Jour about 8 years
      There's no difference, semantically. Just a question of which style one prefers.
    • Some programmer dude
      Some programmer dude about 8 years
      In the first one s is a reference to a constant string. In the second s is a reference to a string that is constant. I.e. both are really the same.
    • dragosht
      dragosht about 8 years
    • kamikaze
      kamikaze about 8 years
      The first is more consistent (const is always behind the type it refers to, which is the only way it works for pointers). The second is more common. The first allows you to read declarations from right to left: s is a references to an immutable std::string.
  • Aviv
    Aviv about 8 years
    Downvoters - I found our where I was mistaken, with little to no thanks to you.