2

Consider the following situation

class A
{
public:
    void someFunc() const
    {
         b->nonConstFunction(); //this is a non const function in the class B
    }

private:
    B* b;
};

In the constant function someFunc() I can call any nonconstant method of b and it will compile. So the function someFunc() is somehow not a true const function. But all best practices says that one should put const anywhere you can. Should this advice be applied also here? May be declaring the function void someFunc() nonconstant is more honest then void someFunc() const. Or may be there is a more general advice to handle this kind of things?

sashoalm
  • 75,001
  • 122
  • 434
  • 781
David
  • 157
  • 1
  • 8
  • 1
    It really depends on what `class A` represents. There's no one size fits all here. – juanchopanza Oct 03 '16 at 14:36
  • take a look at [`mutable`](http://en.cppreference.com/w/cpp/language/cv) => it's perfectly for some case like that: define a variable which can be modified in a const environment because it's doesn't care regardless of class instance (IMHO, a good example is mutex => mutex has to be modified in a const env) – Garf365 Oct 03 '16 at 14:37
  • 4
    If you have access to a copy, you might want to read the item in Effective C++ about logical vs. bitwise constness; that's exactly the confusion you have at the moment. [This question](http://stackoverflow.com/questions/3830367/difference-between-logical-and-physical-const-ness#3830484) has some discussion on it. – TartanLlama Oct 03 '16 at 14:38
  • 2
    "not a true `const` function" and yet `b` isn't modified right? So it depends. Also relevant might be the proposed [`propagate_const`](http://en.cppreference.com/w/cpp/experimental/propagate_const) – Barry Oct 03 '16 at 14:39
  • 1
    Generally use const to mark methods that don't alter the object. Unfortunately some objects cache requests for data, so a method is logically const in terms of external behaviour but internally alters the object. Reserve mutable for that relatively rare situation. – Malcolm McLean Oct 03 '16 at 14:48
  • Avoiding the definitions of const which are a little out of scope. If this is something that concerns you, then use references. – UKMonkey Oct 03 '16 at 14:49
  • 1
    Please fix the title of your question so that it is useful and describes the question. – Lightness Races in Orbit Oct 03 '16 at 14:59

3 Answers3

4

is somehow not a true const function

well, it depends what you want to express.

  1. if A's state logically depends on b's state (this is the implication I get from your question)

    • simplest solution is to give A a member of type B, instead of using either a pointer or a reference. Then you have a const B in your const method, and this expresses your requirement directly

    • next simplest is to just declare the member as const B *b; in the first place - obviously this doesn't work if you also need non-const methods of A to call non-const methods on b.

    • if you need the indirection, another possibility is to use a smart pointer class (I'm not sure if a suitable one already exists) with operator overloads like

       B* operator->();
       const B* operator->() const;
      

      which would give the guarantee you want

  2. if A's state does not logically depend on b's.

    In this case you don't have a problem: mutating b in a const method is fine, since A's logical state doesn't change. This might often be the case where you have a pointer to eg. a logger, which would otherwise need lots of mutable members.

Useless
  • 64,155
  • 6
  • 88
  • 132
2

It's const if A does not "own" B. Modern C++ expresses that by making b a unique_ptr is A is the owner and a plain pointer if just a link.

Malcolm McLean
  • 6,258
  • 1
  • 17
  • 18
1

It would seem that C++ views B as not being part of A but that A is connected to B. As B is not const then A can call its non-const methods. But because A is const you can't make A point to a different B. If you make B a member then you can no longer call its non-const methods because its now part of A.

I thought using a std::unique_ptr would fix this but apparently std::unique_ptr returns a non-const pointer from its const arrow operator ->.

You could fix this by creating a pointer wrapper that passes the constness on to the contained pointer, something like this:

template<typename T>
class const_correct_ptr
{
public:
    const_correct_ptr(std::unique_ptr<T>): p(std::move(p)) {}

    // pass constness onto target
    T* operator->() { return p.get(); }
    T const* operator->() const { return p.get(); }

private:
    std::unique_ptr<T> p;
};

class B
{
public:
    void nonConstFunction() {}
};

class A
{
public:
    A(): b(std::make_unique<B>()) {}

    void someFunc() const
    {
         b->nonConstFunction(); // compile time error!
    }

private:
    const_correct_ptr<B> b;
};
Galik
  • 47,303
  • 4
  • 80
  • 117