确保在编译期间仅将同一工厂中的对象一起使用

时间:2018-08-27 17:36:22

标签: c++

我想确保不能Thing创建由Thing::Factory的不同实例创建的combine的实例。

下面的代码可以在运行时起作用:

#include <cassert>

struct Thing {
  struct Factory {
    Thing make() {return Thing(this);}
  };

  static void combine(Thing t1, Thing t2) {
    assert(t1.factory == t2.factory);
  }

private:
  Thing(Factory* factory_) : factory(factory_) {}
  Factory* factory;
};

int main() {
  Thing::Factory f1;
  Thing t11 = f1.make();
  Thing t12 = f1.make();
  Thing::combine(t11, t12);

  Thing::Factory f2;
  Thing t21 = f2.make();
  Thing t22 = f2.make();
  Thing::combine(t21, t22);

  Thing::combine(t11, t21); // Assertion failure
}

问题:有没有办法在编译过程中做到这一点?

我尝试将Thing用作模板:

template<typename Tag>
struct Thing {
  // Same code as before
};

并将客户端代码修改为:

struct Tag1;
struct Tag2;

int main() {
  Thing<Tag1>::Factory f1;
  Thing<Tag1> t11 = f1.make();
  Thing<Tag1> t12 = f1.make();
  Thing<Tag1>::combine(t11, t12);

  Thing<Tag2>::Factory f2;
  Thing<Tag2> t21 = f2.make();
  Thing<Tag2> t22 = f2.make();
  Thing<Tag2>::combine(t21, t22);
}

然后无法访问combine t11t21。好吧。

但是仍然存在问题:

  • 没有禁止使用Thing<Tag1>::Factory创建另一个combineThing make的{​​{1}}
  • 客户端必须手动声明标签类型

是否存在可以解决这些问题的模式?

2 个答案:

答案 0 :(得分:0)

  
      
  • 没有什么可以禁止创建另一个Thing :: Factory并将其与t11结合使用的东西
  •   

您可以有一个通用的工厂基础,该基础保留在工厂构造函数中初始化的标记std::set实例的静态std::type_info

如果该条目已经在该std::set中,则可以引发运行时异常

  
      
  • 客户端必须手动声明标签类型
  •   

您也可以为工厂实例生成ID,而不必使用模板参数。

答案 1 :(得分:0)

在回答之前,听起来好像您正在使用X-Y problem;并且-我怀疑您是否选择了正确的Y。请考虑完全删除这些工厂,或者-仅将一个工厂用于具有相同基类的许多类。


要在编译时实现这一点,要么工厂需要产生不同类型的值,要么combine()必须能够在编译时运行。但是使combine()成为constexpr并没有帮助,因为工厂只在运行时产生输出...所以,是的,类型区分是您唯一的选择。我想可以使用某种类型的标记-但是,如果您可以事先提供这些标记,那么为什么不从一开始就拥有不同的类型?就像您自己说的一样,不清楚这些标签的来源。

无论如何,要禁止创建两个相同类型的工厂,可以使用singleton pattern。无论如何,工厂都可以在运行时运行,也可以在运行时创建它。