模拟虚方法的构造函数行为

Simulate constructor behaviour for virtual methods

我目前正在使用 C++ 开发一个小型私人项目,我想出了以下结构:

#include <iostream>

class A
{
    std::vector<int> vec;

protected:
    virtual bool onAdd(int toAdd) {
        // should the 'adding' be suppressed?
        // do some A specific checks
        std::cout << "A::onAdd()" << std::endl;
        return false; 
    }

public:
    void add(int i) {
        if(!onAdd(i)) {
            // actual logic
            vec.push_back(i);
        }
    }
};

class B : public A
{
protected:
    bool onAdd(int toAdd) override {
        // do some B specific checks
        std::cout << "B::onAdd()" << std::endl;
        return false;
    }
};

在此示例中,onAdd 基本上是 add 的回调,但采用的是一种更加多态的方式。

当 class C 继承自 B 并想覆盖 onAdd 时,实际问题出现了。在这种情况下,B 中的实现在调用 C::add 时将被丢弃(即不被调用)。所以基本上我想实现的是一种类似构造函数的行为,我可以在 class 层次结构中的不同位置覆盖相同的方法,并且所有这些方法都被调用。

我现在的问题是:有没有possibility/design来实现这个?不过,我确信它不会像级联构造函数那样简单。

注意:不要过分关注add示例。问题是关于类似回调的结构,而不是它是否对 add.

有意义
struct RunAndDiscard {
  template<class Sig, class...Args>
  void operator()(Sig*const* start, Sig*const* finish, Args&&...args)const{
    if (start==finish) return;
    for (auto* i = start; i != (finish-1); ++i) {
      (*i)(args...);
    }
    (*(finish-1))(std::forward<Args>(args)...);
  }
};
template<class Sig, class Combine=RunAndDiscard>
struct invokers {
  std::vector<Sig*> targets;
  template<class...Args>
  decltype(auto) operator()(Args&&...args)const {
    return Combine{}( targets.data(), targets.data()+targets.size(), std::forward<Args>(args)... );
  }
};

struct AndTogetherResultWithShortCircuit {
  template<class Sig, class...Args>
  bool operator()(Sig*const* start, Sig*const* finish, Args&&...args)const{
    if (start==finish) return true;
    for (auto* i = start; i != (finish-1); ++i) {
      if (!(*i)(args...)) return false;
    }
    return (*(finish-1))(std::forward<Args>(args)...);
  }
};

这会创建每个实例 table 待办事项 onAdd

创建每个 class table 更难;您需要将 table 与父类型的 table 链接起来,这需要每个 class 样板文件。

如果不自己动手,就无法让 C++ 编译器编写每个实例版本或每个 class 版本。

有涉及反射和具体化的 C++20 提议,以及元class 提议,它可能涉及像这样自动编写代码(在每个实例和每个 class基础)。

Here 是该技术正在测试的一个实例:

struct AndTogetherResultWithShortCircuit {
  template<class Sig, class...Args>
  bool operator()(Sig*const* start, Sig*const* finish, Args&&...args)const{
    if (start==finish) return true;
    for (auto* i = start; i != (finish-1); ++i) {
      if (!(*i)(args...)) return false;
    }
    return (*(finish-1))(std::forward<Args>(args)...);
  }
};
class A {
  std::vector<int> vec;
protected:
  invokers<bool(A*, int), AndTogetherResultWithShortCircuit> onAdd;
public:
  void add(int i) {
    if (!onAdd(this, i)) {
      vec.push_back(i);
    }
  }
};
class B : public A
{
public:
   B() {
     onAdd.targets.push_back([](A* self, int x)->bool{
       // do some B specific checks
       std::cout << "B::onAdd(" << x << ")" << std::endl;
        return x%2;
     });
   }
};
class C : public B
{
public:
   C() {
     onAdd.targets.push_back([](A* self, int x)->bool{
       // do some B specific checks
       std::cout << "C::onAdd(" << x << ")" << std::endl;
       return false;
     });
   }
};

当您想编写自己的 OO 系统时,可以使用 C++,但 C++ 不会为您编写。

以下解决方案使用 std::function 在每个构造函数中添加每个回调:

#include <iostream>
#include <vector>
#include <functional>

class A
{
    std::vector<int> vec;

protected:
    bool onAdd(int toAdd) 
    {
        // do some A specific checks
        std::cout << "A::onAdd()" << std::endl;
        return true;
    }

    // vector of callback functions. Initialized with A::onAdd() callback as the first entry
    std::vector<std::function<bool(int)>> callbacks{{[this](int toAdd){return onAdd(toAdd); }}};

public:
    void add(int i) 
    {
        for(auto& callback : callbacks) {
            if(!callback(i))
                return;
        }
        // actual logic
        vec.push_back(i);
    }
};

class B : public A
{
public:
    B()
    {
        callbacks.emplace_back([this](int toAdd){return onAdd(toAdd); });
    }
protected:
    bool onAdd(int toAdd)  
    {
        // do some B specific checks
        std::cout << "B::onAdd()" << std::endl;
        return true;
    }
};

class C : public B
{
    public:
    C()
    {
        callbacks.emplace_back([this](int toAdd){return onAdd(toAdd); });
    }
protected:
    bool onAdd(int toAdd)  
    {
        // do some C specific checks
        std::cout << "C::onAdd()" << std::endl;
        // must also call B::onAdd()
        return true;
    }

};

int main()
{
    C c;
    c.add(5);
}

打印:

A::onAdd()
B::onAdd()
C::onAdd()

我会调用我的 parents onAdd()

bool C::onAdd(int toAdd) {return my_answer && B::onAdd(toAdd);}

如果您希望其他开发人员继承您的基础 class,这可能会有点令人困惑。但对于小型私有层次结构来说,它工作得很好。

我有时会包含一个 using 语句来使其更加明确

class C : public B
{
  using parent=B;
  bool onAdd(int toAdd) override {return my_answer && parent::onAdd(toAdd);}
};

如果您想要一个通用的解决方案,也许您可​​以使用带有可变参数模板的 CRTP 而不是运行时多态性。

this answer and this answer 中汲取灵感:

template<class... OnAdders> class A : private OnAdders... {
    std::vector<int> vec;

   template<class OnAdder>
   bool onAdd(int toAdd){
     return static_cast<OnAdder*>(this)->onAdd(toAdd);
   }

   template<typename FirstOnAdder, typename SecondOnAdder, class... RestOnAdders>
   bool onAdd(int toAdd){
     if (onAdd<FirstOnAdder>(toAdd))
        return true;

     return onAdd<SecondOnAdder, RestOnAdders...>(toAdd);
   }

public:
    void add(int i) {      
        if (onAdd<OnAdders...>(i))
          return;       

        // actual logic
        vec.push_back(i);
    }
};

class B {
public:
    bool onAdd(int toAdd) {
        // do some B specific checks
        std::cout << "B::onAdd()" << std::endl;
        return false;
    }
};

你可以像这样使用:

A<B,C> a;
a.add(42);

Live demo.