1

我有一个类层次结构,我想使用多态性来调用正确的成员函数。在基本层面上这是可行的,但是在尝试使用 Mixin 类来扩展或更改某些类的功能时遇到了问题。基本上,我想在构造从 mixin 继承的对象时对成员值进行一些验证。(我来自 python 背景,很容易创建改变构造函数行为的混合。方法解析顺序保证从构造函数调用的函数首先从派生类调用)在 C++ 中,动态绑定在构造函数中被禁用(我明白原因)。对virtual void init()函数的调用将不起作用,因为总是会调用基类函数。

有什么方法可以保证validate()函数被执行而不需要再次显式定义构造函数?

工厂是一种方法,但我也希望构造函数具有不同的类型参数。

下面显示了一个最小的示例。

非常感谢

class Base 
{
  Base(){};
  //... some virtual functions...
}

class Derived: public Base
{
  using Base::Base;
  // new constructors
  Derived(some pars){};
  Derived(other pars){};
  //... some virtual functions...
}

template <class B>
class Mixin: public B
{
  using B::B;
  Mixin()
  { 
    // mixin related constructor code
    // maybe some member validation or discretization of a continuous value
    // hides B::B(), but is only called if the default constructor is called, not for B::B(pars)
    this->validate();
  }
  void validate(){};
}

class UsingMixin: public Mixin<Derived>
{
  using Mixin::Mixin; // inherit all constructors
  // I want to avoid defining the same constructors from Derived again,
  // since there would be no change

  // some functions
}

编辑:实现这一点的一种方法是在 mixin 上使用模板构造函数,但我不知道这种方法的安全性和可用性,因为我需要知道基类中构造函数参数的最大数量。


template <class B>
class Mixin: public B
{
  template <class Arg0>
  Mixin(Arg0 arg0)
      : B(arg0)
  {
      this->validate();
  }

  template <class Arg0, class Arg1>
  Mixin(Arg0 arg0, Arg1 arg1)
      : B(arg0, arg1)
  {
      this->validate();
  }

  template <class Arg0, class Arg1, class Arg2>
  Mixin(Arg0 arg0, Arg1 arg1, Arg2 arg2)
      : B(arg0, arg1, arg2)
  {
      this->validate();
  }


  template <class Arg0, class Arg1, class Arg2, class Arg3>
  Mixin(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3)
      : B(arg0, arg1, arg2, arg3)
  {
      this->validate();
  }

  void validate(){}
}
4

1 回答 1

1

您可以尝试使用可变参数模板和完美转发创建您的 Mixin 构造函数,因此您不必为每个可能的参数数量定义一个版本。

struct B : public A {
    template<typename... Args>
    B(Args&&... args) : A(std::forward<Args>(args)...) { this->validate(); }
};

你有没有想过给 Mixin 一个“验证器”类类型的成员变量,它的构造函数接受一个指向 Mixin 的指针并调用validate它?这样,您不需要为 Mixin 创建一个构造函数(只需为您的“验证器”成员定义一个默认初始化程序),它将在您的 Mixin 构造函数的确切时间运行。

struct B : public A {
    using A::A;
    struct V { V(B & b) { b.validate(); } };
    V v_ = { *this };
};

使用 C++20 的[[no_unique_address]] https://en.cppreference.com/w/cpp/language/attributes/no_unique_address,您甚至不必为拥有该空成员支付任何内存损失。

方法解析顺序保证从构造函数调用的函数首先从派生类调用)在C++中动态绑定在构造函数中被禁用(我理解原因)。对虚拟 void init() 函数的调用将不起作用,因为总是会调用基类函数。

不知道你是什么意思。请参阅此示例:https ://godbolt.org/z/RVSkpi

#include <iostream>
struct A {
    virtual int a() { std::cout << "A::a\n"; return 1; }
    virtual int b() { std::cout << "A::b\n"; return a(); }
};
struct B : public A {
    virtual int a() { std::cout << "B::a\n"; return 2; }
    virtual int b() { std::cout << "B::b\n"; return a(); }
    B() : a_(b()) { b(); }
    int a_;
};
int main() {
    B b;
    return 0;
}

在执行成员的第一个构造函数之前B(以及在构造函数A完成执行之后),正在构造的对象“变成” type B,并且一直保持这种状态直到 of 的构造函数结束B(之后它可能变成其他一些继承自B)。在构造函数中,根本不需要虚拟查找,因为编译器知道类型是精确的B,并且可以静态解析方法调用。但它不能为a()from的调用做到这一点b(),因为它不仅可以从构造函数调用。但是,由于在b()示例中将调用时间,对象的动态类型是B,这些也将被解析为调用B::a在运行时。

编辑:如果你想让一个进一步的派生类提供验证功能,如评论中所述,并且你没有 C++20,你可以尝试这样的事情:https ://godbolt.org/z/ r23xJv

#include <iostream>
struct A {
    A(int a) : a_(a) {}
    int a_;
};
template<typename T, typename VF>
struct B : T {
    using A::A;
    struct V { V(B & b) { VF::verify(b); } };
    V v_ = { *this };
};
struct C : B<A, C> {
    using B::B;
    static void verify(B & b) { std::cout << b.a_ << "\n"; }
};
int main(int argc, char* argv[]) {
    C c(123);
    return 0;
}
于 2020-04-28T13:32:51.103 回答