How to generate a non-const method from a const method?

后端 未结 11 2421
时光取名叫无心
时光取名叫无心 2021-02-08 11:18

While striving for const-correctness, I often find myself writing code such as this

class Bar;

class Foo {
public:
  const Bar* bar() const { /* code that gets          


        
相关标签:
11条回答
  • 2021-02-08 11:38

    Assuming you accept const-correctness as a technique, then I think that means you prefer compiler-checked const-correctness to brevity. So you want the compiler to check two things:

    1. That when "this" is non-const, the referand of the pointer you return can safely be modified by the caller.
    2. That when "this" is const, whatever work you do does not perform any non-const operations on "this".

    If the const version calls the non-const then you don't get (2). If the non-const version calls the const one and const_casts the result, then you don't get (1). For instance suppose Bar is actually char, and the code you write ends up returning (in some cases) a string literal. This will compile (and -Wwrite-strings gives no warning), but your caller ends up with a non-const pointer to a string literal. This contradicts "you prefer compiler-checked const-correctness".

    If they both call a helper member function Bar *getBar() const, then you get both (1) and (2). But if it's possible to write that helper function, then why are you messing about with const and non-const versions in the first place, when it's perfectly OK to modify the Bar returned from a const Foo? Occasionally perhaps some detail of implementation means you're implementing an interface with two accessors even though you only need the one. Otherwise either the helper can't be written, or else the two functions can be replaced just by the single helper.

    As long as code size is not a concern, I think the best way to achieve both (1) and (2) is to have the compiler actually consider both cases:

    struct Bar { int a; };
    struct Foo {
              Bar *bar()       { return getBar<Bar>(this); }
        const Bar *bar() const { return getBar<const Bar>(this); }
    
        Bar *bar2() const { return getBar<Bar>(this); } // doesn't compile. Good.
        Bar *bar3() const { return getBar<const Bar>(this); } // likewise
    
        private:
        template <typename B, typename F>
        static B *getBar(F *self) {
            // non-trivial code, which can safely call other functions with 
            // const/non-const overloads, and we don't have to manually figure out
            // whether it's safe to const_cast the result.
            return &self->myBar;
        }
        Bar myBar;
    };
    

    If the code is trivial, like an operator[] which accesses some array owned by the object, then I would just duplicate the code. At some point, the above function template is less coding effort than the duplication, at which point use the template.

    I think the const_cast approach, while clever and seemingly standard, just isn't helpful, because it chooses brevity over compiler-checked const-correctness. If the code in the method is trivial, then you can duplicate it. If it's not trivial, then it's not easy for you or a code maintainer to see that the const_cast is actually valid.

    0 讨论(0)
  • 2021-02-08 11:39

    Your code implies that the const bar() is actually creating and returning a new Bar, and I find that peculiar if you're doing that lot.

    For me a bigger problem is that constness in a member function is limited to reference and non-pointer members. The moment you have (non-const) pointer members, a const function can change them even though it claims to be const on the box.

    If your Bar instance is a member variable, consider returning a reference instead.

    0 讨论(0)
  • 2021-02-08 11:42

    FYI - The code posted by the OP is the preferred method given in Scott Meyers' "Effective C++ - Third Edition". See Item #3.

    0 讨论(0)
  • 2021-02-08 11:44

    You can do something like this:

    class Bar;
    
    class Foo {
    public:
      const Bar* bar() const { return getBar(); }
    
      Bar* bar() {
       return getBar();
      }
    
      private:
        Bar* getBar() const {/* Actual code */ return NULL;}
    };
    
    0 讨论(0)
  • 2021-02-08 11:46

    Use following trick:

    
    class Bar;
    class Foo {
    public:  
      Bar* bar() { 
        // non-const case
        /* code that does something */ 
      }  
      const Bar* bar() const {    
          return This().bar();  // use non-const case
       }
    
    private:
      //trick: const method returns non-const reference
      Foo & This() const { return const_cast<Foo &>(*this); } 
    };
    

    Note it is possible to use unique function This for any const/non-const functions.

    Alternative solution without static_cast (but I prefer the first one):

    class Bar;
    class Foo {
    public:  
      const Bar* bar() const { /* code that does something */ }  
      Bar* bar() { return const_cast<Bar*>(cthis().bar()); } // use const case
    private:
      const Foo & cthis() const { return *this; } 
    };
    
    0 讨论(0)
提交回复
热议问题