How do I remove code duplication between similar const and non-const member functions?

后端 未结 19 1706
天涯浪人
天涯浪人 2020-11-22 00:30

Let\'s say I have the following class X where I want to return access to an internal member:

class Z
{
    // details
};

class X
{
    std::vec         


        
相关标签:
19条回答
  • 2020-11-22 00:58

    Didn't find what I was looking for, so I rolled a couple of my own...

    This one is a little wordy, but has the advantage of handling many overloaded methods of the same name (and return type) all at once:

    struct C {
      int x[10];
    
      int const* getp() const { return x; }
      int const* getp(int i) const { return &x[i]; }
      int const* getp(int* p) const { return &x[*p]; }
    
      int const& getr() const { return x[0]; }
      int const& getr(int i) const { return x[i]; }
      int const& getr(int* p) const { return x[*p]; }
    
      template<typename... Ts>
      auto* getp(Ts... args) {
        auto const* p = this;
        return const_cast<int*>(p->getp(args...));
      }
    
      template<typename... Ts>
      auto& getr(Ts... args) {
        auto const* p = this;
        return const_cast<int&>(p->getr(args...));
      }
    };
    

    If you have only one const method per name, but still plenty of methods to duplicate, then you might prefer this:

      template<typename T, typename... Ts>
      auto* pwrap(T const* (C::*f)(Ts...) const, Ts... args) {
        return const_cast<T*>((this->*f)(args...));
      }
    
      int* getp_i(int i) { return pwrap(&C::getp_i, i); }
      int* getp_p(int* p) { return pwrap(&C::getp_p, p); }
    

    Unfortunately this breaks down as soon as you start overloading the name (the function pointer argument's argument list seems to be unresolved at that point, so it can't find a match for the function argument). Although you can template your way out of that, too:

      template<typename... Ts>
      auto* getp(Ts... args) { return pwrap<int, Ts...>(&C::getp, args...); }
    

    But reference arguments to the const method fail to match against the apparently by-value arguments to the template and it breaks. Not sure why.Here's why.

    0 讨论(0)
  • 2020-11-22 01:01

    For a detailed explanation, please see the heading "Avoid Duplication in const and Non-const Member Function," on p. 23, in Item 3 "Use const whenever possible," in Effective C++, 3d ed by Scott Meyers, ISBN-13: 9780321334879.

    alt text

    Here's Meyers' solution (simplified):

    struct C {
      const char & get() const {
        return c;
      }
      char & get() {
        return const_cast<char &>(static_cast<const C &>(*this).get());
      }
      char c;
    };
    

    The two casts and function call may be ugly but it's correct. Meyers has a thorough explanation why.

    0 讨论(0)
  • 2020-11-22 01:01

    I'd suggest a private helper static function template, like this:

    class X
    {
        std::vector<Z> vecZ;
    
        // ReturnType is explicitly 'Z&' or 'const Z&'
        // ThisType is deduced to be 'X' or 'const X'
        template <typename ReturnType, typename ThisType>
        static ReturnType Z_impl(ThisType& self, size_t index)
        {
            // massive amounts of code for validating index
            ReturnType ret = self.vecZ[index];
            // even more code for determining, blah, blah...
            return ret;
        }
    
    public:
        Z& Z(size_t index)
        {
            return Z_impl<Z&>(*this, index);
        }
        const Z& Z(size_t index) const
        {
            return Z_impl<const Z&>(*this, index);
        }
    };
    
    0 讨论(0)
  • 2020-11-22 01:03

    Typically, the member functions for which you need const and non-const versions are getters and setters. Most of the time they are one-liners so code duplication is not an issue.

    0 讨论(0)
  • 2020-11-22 01:04

    How about moving the logic into a private method, and only doing the "get the reference and return" stuff inside the getters? Actually, I would be fairly confused about the static and const casts inside a simple getter function, and I'd consider that ugly except for extremely rare circumstances!

    0 讨论(0)
  • 2020-11-22 01:05

    To add to the solution jwfearn and kevin provided, here's the corresponding solution when the function returns shared_ptr:

    struct C {
      shared_ptr<const char> get() const {
        return c;
      }
      shared_ptr<char> get() {
        return const_pointer_cast<char>(static_cast<const C &>(*this).get());
      }
      shared_ptr<char> c;
    };
    
    0 讨论(0)
提交回复
热议问题