Partners: KDAB Whole Tomato Software CppDepend

22 January 2018

How to propagate const on a pointer data member?

propagate_const, C++

Inside const methods all member pointers become constant pointers.
However sometimes it would be more practical to have constant pointers to constant objects.

So how can we propagate such constness?

The problem

Let’s discuss a simple class that keeps a pointer to another class. This member field might be an observing (raw) pointer, or some smart pointer.

class Object
{
public:
    void Foo() { }
    void FooConst() const { }
};

class Test
{
private:
    unique_ptr<Object> m_pObj;

public:
    Test() : m_pObj(make_unique<Object>()) { }

    void Foo() { 
        m_pObj->Foo();
        m_pObj->FooConst();
    }

    void FooConst() const { 
        m_pObj->Foo();
        m_pObj->FooConst();
    }
};

We have two methods Test::Foo and Test::FooConst that calls all methods (const and non-const) of our m_pObj pointer.

Can this compile?

Of course!

So what’s the problem here?

Have a look:

Test::FooConst is a const method, so you cannot modify members of the object. In other words they become const. You can also see it as this pointer inside such method becomes const Test *.

In the case of m_pObj it means you cannot change the value of it (change its address), but there’s nothing wrong with changing value that it’s pointing to. It also means that if such object is a class, you can safely call its non const methods.

Just for the reference:

// value being pointed cannot be changed:
const int* pInt;
int const* pInt; // equivalent form
// address of the pointer cannot be changed, 
// but the value being pointed can be
int* const pInt;
// both value and the address of the 
// pointer cannot be changed
const int* const pInt;
int const* const pInt; // equivalent form

m_pObj becomes Object* const but it would be far more useful to have Object const* const.

In short: we’d like to propagate const on member pointers.

Small examples

Are there any practical examples?

One example might be with Controls:

If a Control class contains an EditBox (via a pointer) and you call:

int Control::ReadValue() const
{
    return pEditBox->GetValue();
}

auto val = myControl.ReadValue();

It would be great if inside Control::ReadValues (which is const) you could only call const methods of your member controls (stored as pointers).

And another example: the pimpl pattern.

Pimpl divides class and moves private section to a separate class. Without const propagation that private impl can safely call non-const methods from const methods of the main class. So such design might be fragile and become a problem at some point. Read more in my recent posts: here and here.

What’s more there’s also a notion that a const method should be thread safe. But since you can safely call non const methods of your member pointers that thread-safety might be tricky to guarantee.

Ok, so how to achieve such const propagation through layers of method calls?

Wrappers

One of the easiest method is to have some wrapper around the pointer.

I’ve found such technique while I was researching for pimpl (have a look here: The Pimpl Pattern - what you should know).

You can write a wrapper method:

const Object* PObject() const { return m_pObj; }
Object* PObject() { return m_pObj; }

And in every place - especially in const method(s) of the Test class - you have to use PObject accessor. That works, but might require consistency and discipline.

Another way is to use some wrapper type. One of such helpers is suggested in the article Pimp My Pimpl — Reloaded | -Wmarc.

In the StackOverflow question: Propagate constness to data pointed by member variables I’ve also found that Loki library has something like: Loki::ConstPropPtr\

propagate_const

propagate_const is currently in TS of library fundamentals TS v2:
C++ standard libraries extensions, version 2.

And is the wrapper that we need:

From propagate_const @cppreference.com:

std::experimental::propagate_const is a const-propagating wrapper for pointers and pointer-like objects. It treats the wrapped pointer as a pointer to const when accessed through a const access path, hence the name.

As far as I understand this TS is already published (even before C++17). Still not all features were merged into C++17... so not sure if that's reach C++20. See this r/cpp comment.

It’s already available in

Here’s the paper:

N4388 - A Proposal to Add a Const-Propagating Wrapper to the Standard Library

The authors even suggest changing the meaning of the keyword const… or a new keyword :)

Given absolute freedom we would propose changing the const keyword to propagate const-ness.

But of course

That would be impractical, however, as it would break existing code and change behaviour in potentially undesirable ways

So that’s why we have a separate wrapper :)

We can rewrite the example like this:

#include <experimental/propagate_const>

class Object
{
public:
    void Foo() { }
    void FooConst() const { }
};

namespace stdexp = std::experimental;

class Test
{
private:
    stdexp::propagate_const<std::unique_ptr<Object>> m_pObj;

public:
    Test() : m_pObj(std::make_unique<Object>()) { }

    void Foo() { 
        m_pObj->Foo();
        m_pObj->FooConst();
    }

    void FooConst() const { 
        //m_pObj->Foo(); // cannot call now!
        m_pObj->FooConst();
    }
};

propagate_const is move constructible and move assignable, but not copy constructable or copy assignable.

Playground

As usual you can play with the code using a live sample:

Summary

Special thanks to author - iloveportalz0r - who commented on my previous article about pimpl and suggested using popagate_const! I haven’t seen this wrapper type before, so it’s always great to learn something new and useful.

All in all I think it’s worth to know about shallow const problem. So if you care about const correctness in your system (and you should!) then propagate_const (or any other wrapper or technique) is very important tool in your pocket.

Get my free ebook about C++17!

More than 50 pages about the new Language Standard.

C++17 in detail, by Bartlomiej Filipek

For now I don't have my own courses, but I promote others :) (Please note, I'll also get a little commission for every signup. That's a huge support for my work!). Have a look my recommended Online Courses at @Pluralsight (more info in my Resource page):

© 2017, Bartlomiej Filipek, Blogger platform
Any opinions expressed herein are in no way representative of those of my employers.
This site contains ads or referral links, which provide me with a commission. Thank you for your understanding.