如何避免语法上相同的const和非const函数之间的代码重复,这些函数在语义上不相同

时间:2017-01-13 13:35:43

标签: c++

#include <iostream>
using namespace std;

class A 
{
    public:
    A() : x(0) {}
    // notice: not identical to const version but does update
    void FA() {std::cout << "A" << std::endl; x++;}
    void FA() const {std::cout << "const A" << std::endl;}
    private:
    int x;
};

class B
{
    public:
    B() : x(0) {}
    // notice: not identical to const version but does update
    void FB() {std::cout << "B" << std::endl; x++;}
    void FB() const {std::cout << "const B" << std::endl;}
    private:
    int x;
};

class C
{
    public:
    void FC()
    {
        bool condition = true; // should be set to a real condition

        if(condition)
        {
            a.FA();
        }
        b.FB();
    }

    void FC() const
    {
        bool condition = true; // should be set to a real condition

        if(condition)
        {
            a.FA();
        }
        b.FB();
    }

    private:
    A a;
    B b;
};

int main() {
    C c;
    c.FC();

    const C cc;
    cc.FC();

    return 0;
}

首先,抱歉这个冗长的标题。 如何在函数 FC FC const 中避免C类中的代码重复?鉴于您不能使用将转换为 const 并从非const FC版本调用const FC版本的技巧,因为非const FC的实体将调用将执行更新的函数与其对应的常量不同。

2 个答案:

答案 0 :(得分:7)

让模板成员函数执行实际工作。换句话说:试试这个:

class C
{
public:
    void FC()
    {
        FC_Impl( *this );
    }

    void FC() const
    {
        FC_Impl( *this );
    }

private:
    template <typename Self>
    static void FC_Impl( Self & self )
    {
      bool condition = true; // should be set to a real condition

      if(condition)
      {
          self.a.FA();
      }
      self.b.FB();
    }

    A a;
    B b;
};

答案 1 :(得分:0)

拉尔夫给出了很好的答案。我认为值得一提的是,提供具有相同名称但具有不同参数类型的自由函数,我们允许自己在高级别表达意图,而自由函数充当适配器。我们允许ADL为我们找到正确的函数重载(或特化)。

通过这种方式,我们不需要知道成员函数的名称以表达逻辑。在这个例子中,它会使类C更容易修改和演化:

#include <iostream>
#include <utility>
using namespace std;

// general case
template<class T> 
  decltype(auto) apply_F(T&& t)
{
  return t.F();
}

// call apply_F with either an X or a Y and return the result )can be void)   
template<class X, class Y>
  auto conditionally_apply_F(bool condition, X&& x, Y&& y)
  -> std::common_type_t<decltype(apply_F(x)), decltype(apply_F(y))>
{
  if (condition) {
    return apply_F(x);
  }
  else {
    return apply_F(y);
  }
}

// provides F member function - no need for specific adapter
class A 
{
    public:
    A() : x(0) {}
    // notice: not identical to const version but does update
    void F() {std::cout << "A" << std::endl; x++;}
    void F() const {std::cout << "const A" << std::endl;}
    private:
    int x;
};


// has differing names implementing the concept of F - so we'll need an adapter
class B
{
    public:
    B() : x(0) {}
    // notice: not identical to const version but does update
    void F_mutable() {std::cout << "B" << std::endl; x++;}
    void F_const() const {std::cout << "const B" << std::endl;}
    private:
    int x;
};

// adapter overloads
decltype(auto) apply_F(B const& t)
{
  return t.F_const();
}

decltype(auto) apply_F(B& t)
{
  return t.F_mutable();
}

// class C current expressed in terms of A and B, but no longer tied to these types    
class C
{
    public:
    void FC()
    {
        bool condition = true; // should be set to a real condition
        conditionally_apply_F(condition, a, b);
    }

    void FC() const
    {
        bool condition = true; // should be set to a real condition
       conditionally_apply_F(condition, a, b);
    }

    private:
    A a;
    B b;
};

int main() {
    C c;
    c.FC();

    const C cc;
    cc.FC();

    return 0;
}