我想确保不能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
t11
和t21
。好吧。
但是仍然存在问题:
Thing<Tag1>::Factory
创建另一个combine
和Thing
make
的{{1}} 是否存在可以解决这些问题的模式?
答案 0 :(得分:0)
- 没有什么可以禁止创建另一个Thing :: Factory并将其与t11结合使用的东西
您可以有一个通用的工厂基础,该基础保留在工厂构造函数中初始化的标记std::set
实例的静态std::type_info
。
如果该条目已经在该std::set
中,则可以引发运行时异常
- 客户端必须手动声明标签类型
您也可以为工厂实例生成ID,而不必使用模板参数。
答案 1 :(得分:0)
在回答之前,听起来好像您正在使用X-Y problem;并且-我怀疑您是否选择了正确的Y。请考虑完全删除这些工厂,或者-仅将一个工厂用于具有相同基类的许多类。
要在编译时实现这一点,要么工厂需要产生不同类型的值,要么combine()
必须能够在编译时运行。但是使combine()
成为constexpr并没有帮助,因为工厂只在运行时产生输出...所以,是的,类型区分是您唯一的选择。我想可以使用某种类型的标记-但是,如果您可以事先提供这些标记,那么为什么不从一开始就拥有不同的类型?就像您自己说的一样,不清楚这些标签的来源。
无论如何,要禁止创建两个相同类型的工厂,可以使用singleton pattern。无论如何,工厂都可以在运行时运行,也可以在运行时创建它。