Reputation: 123
Every once in a while I get hung up by this and no one has really been able to give a satisfying answer.
In a (admittedly contrived) example like this...
typedef std::string _str;
class MyClass
{
public:
MyClass(const _str &s) : m_str(s) { }
private:
const _str &m_str;
}
... Things work pretty much as expected. The member m_str
can only be assigned in the constructor initializer list and cannot be reassigned later on, and non-const iterators are unavailable. Okay, then. But if we do this instead...
typedef std::string _str;
typedef std::string &_strref;
class MyClass
{
public:
MyClass(const _strref s) : m_str(s) { }
void test() { m_str.append(std::string("why am I not const?")); }
private:
const _strref m_str;
}
... The const gets dropped. Non-cost methods may be called on m_str
and it may be reassigned, which is misleading considering it has const
written next to it.
Upvotes: 3
Views: 781
Reputation: 171363
The const gets "dropped" because you don't have a reference to a const string, you have a const-reference to a non-const string, and a const-reference is meaningless, references can never be re-bound anyway, they're bound to a single object once and stay that way.
i.e. your
const _strref m_str;
is not
const string& m_str;
but rather
string& const m_str;
and that type doesn't make sense, so the const
gets dropped and it's just
string& m_str;
Put another way, you can't "insert" the const
"inside" the typedef. The typedef is string&
, a reference to a string
, you can't change it to be a reference to a different type (a const string
) by putting const
before it ... that can't change the type of object the reference refers to.
The best solution, as pointed out by one of the comments, is don't use typedefs for references, it gains you nothing. If you use _str&
instead of _strref
then you can add const
without problems, as either const _str&
or _str const&
Upvotes: 4
Reputation: 45695
The problem is what we understand by saying "const reference". It's a bit confusing. It's not the reference which should be const, but the referenced object:
const std::string &
should be read as "reference to a const string" (but not "const reference to a string").
You defined a type-alias for a string reference. You then instantiated a const reference to such. But references are always const, so your const
is ignored. The string referenced by this reference is not const.
It's more or less similar to this problem: A pointer to a const-string makes sure you don't modify the string behind the pointer. A const pointer to a non-const-string makes sure you don't re-seat the pointer (which is not possible with references, so when we say "const reference" we always mean the first case!)
So to put things in a nutshell: When type-aliasing a reference you have to care about const-ness already, not later (the same holds for pointers, by the way). So you need another typedef for a reference to a const string, like this:
typedef const std::string &_strcref;
Upvotes: 7
Reputation: 171167
The core of the issue is that a typedef introduced something like "parentheses" into type definitions with respect to qualifiers such as const
.
That is, const std::string &
means:
Apply reference to a type A, which is const
applied to a type B, which is std::string
.
However, const _strref
means:
Apply const
to a type A, which is _strref
, which is a reference to std::string
.
In other words, the non-typedef version gives a reference to a constant string, while the typedef version yields a "constant" reference to a mutable string. Since references themselves cannot be constant or nonconstant, the const
gets simply dropped.
If you really want to use such typedefs, you'll have to put the const
into the typedef:
typedef const std::string & _cstrref;
However, I think reference typedefs (and to a lesser degree, pointer typedefs as well) worsen readability of a program. When I see a function declaration without &
in front of a parameter, I expect passing by value occurs.
Upvotes: 4