是否有一种规范的方法来处理两个外部定义的 类 之间的显式转换?

Is there a canonical way to handle explicit conversion between two externally-defined classes?

我正在使用两个外部库,它们定义了具有相同内容的 classes(比方说 Armadillo's Arma::vec and Eigen's Eigen::VectorXd)。我希望能够尽可能干净地在这些 class 之间进行转换。

如果我定义了 class,那么在 class 定义中包含一个构造函数或转换运算符就很简单了,这样我就可以写出例如

Arma::vec foo(/*some constructor arguments*/);
Eigen::VectorXd bar = Eigen::VectorXd(foo);

但是由于两个 classes 都来自外部库,我不能这样做。如果我尝试编写一个简单的转换函数,例如

class A{
public:
  int value_;
  A(int value) : value_(value) {}
};

class B{
public:
  int value_;
  B(int value) : value_(value) {}
};

A A(const B& b){return A(b.value_);}

int main(void){
  A a(1);
  B b(2);
  a = A(b);
}

然后函数隐藏了 class 定义,突然间我根本无法使用 A class。

我知道允许定义 A a=b 会是 ,但我不明白为什么允许 A a=A(b) 会导致任何问题。

我的问题:

是否可以编写函数或运算符来允许语法 A a=A(b)?如果没有,是否有进行这种转换的规范方法?

我在一些库中看到 A a=toA(b),但这并没有被一致使用,我不喜欢与通常的类型转换不一致。

Is it possible to write a function or operator to allow the syntax A a=A(b)?

不,这是不可能的。所涉及的两个 class 定义了哪些转换是可能的,您不能在定义后更改 class 定义。

您将需要使用给定示例中的函数,但我会避免重复类型名称并编写

auto a = toA(b);

TL;DR

最佳工程实践是通过引入消耗 Eigen::VectorXd 和 returns Arma::vec.[=25 的函数(或实用程序 class)来使用 design pattern Factory =]

Arma::vec createFrom(Eigen::VectorXd from) { ... }

任何其他黑客攻击都是浪费时间和引入迟早会反击的紧密耦合。 松耦合在SW工程中是必不可少的。

详细

您可以引入目标 class 的后代,您可以在其中定义一个构造函数,就像您描述的那样:

class MyArma : Arma::vec {
public:
    MyArma(Eigen::VectorXd from) : x(from.x), y(from.y), z(from.z) {
        /* empty constructor as we are fine with initializers */
    }
}

然后您就可以根据 Eigen 的向量创建 Arma 向量,例如Arma 类型数组

Arma::vec vecArray[] = { MyArma(eigenVect1), MyArma(eigenVect2) };

这来自于继承的原则。或者,您可以使用称为 Decorator 的设计模式,其中原始向量 (Eigen) 隐藏在当前向量 (Armadillo) 的界面后面。这涉及覆盖所有方法,并且必须没有 public 属性,并且所有方法都必须被声明为虚拟...这么多条件。

然而,上述设计存在一些工程缺陷。您正在使用 Virtual Method Table 添加性能开销,您正在为此目的维护相当大且敏感的库。最重要的是:你会产生技术依赖性——所谓的意大利面条。一个对象不应该知道替代方案。

armadillo 的文档很好地提示您应该使用名为 Factory 的设计模式。 Factory 是一个独立的 class 或一个函数,它结合了两种实现的知识并包含从一个实现中提取信息并构造另一个实现的算法。

基于http://arma.sourceforge.net/docs.html#imbue,你最好创建一个工厂class,它创建与输入向量大小相同的目标向量,并使用方法imbue(...)设置值基于输入向量中相应元素的单个元素。

class ArmaVecFacotry() {
   Arma::vec createFrom(Eigen::VectorXd from) {
      Arma::vec armaVec(from.size(), fill::none);
      int currentElement = 0;
      armaVec.imbue( [&]() { return from(currentElement++); } );
      return armaVec;
   }
}

然后简单地创建像

这样的对象
Eigen::VectorXd sourceVector;
Arma::vec tergetvector = std::move(ArmaVecFactory::createFrom(sourceVector));

备注:

  • 您可以在 lambda 表达式之外设置 currentElement 计数器,因为它被 [&]
  • 捕获
  • 我在堆栈上创建向量,但在外部 std::move 确保内存得到有效使用而没有过度复制。