user2285060
user2285060

Reputation: 123

How to preserve const-ness of typedef'd types

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

Answers (3)

Jonathan Wakely
Jonathan Wakely

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

leemes
leemes

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

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

Related Questions