如何创建指向模板不同实例化的指针 class
How to create pointer to different instantiation of template class
在用 C++ 进行抽象工厂的第一次实验时(在阅读现代 C++ 设计 - A. Alexandrescu - 第 9 部分时)我有一个问题。
如果 classes 层次结构如下所示:
struct B {};
struct D1 :public B {};
struct DD1 : public D1 {};
struct DD2 : public D1 {};
struct D2 :public B {};
struct DD3 : public D2 {};
struct DD4 : public D2 {};
我有这个抽象工厂代码:
//abstract factory version 1
struct AbstractFactoryImpl
{
virtual D1* CreateD1() = 0;
virtual D2* CreateD2() = 0;
};
struct AbstractFactory : public AbstractFactoryImpl
{
virtual D1* CreateD1() { return new D1; };
virtual D2* CreateD2() { return new D2; };
};
template<class ... Ts>
struct ConcreteFactory :public AbstractFactory
{
using params = std::tuple<Ts...>;
using T1 = typename std::tuple_element_t<0, params>;
using T2 = typename std::tuple_element_t<1, params>;
virtual D1* CreateD1()override
{
static_assert(std::is_base_of_v<D1, T1>);
return new T1;
};
virtual D2* CreateD2()override
{
static_assert(std::is_base_of_v<D2, T2>);
return new T2;
};
};
并像这样在客户端代码中使用它。
//version 1
AbstractFactory* pFactory;
pFactory = new ConcreteFactory<DD1, DD3>;
D1* pD1 = pFactory->CreateD1();
D2* pD2 = pFactory->CreateD2();
pFactory = new ConcreteFactory<DD2, DD4>;
pD1 = pFactory->CreateD1();
pD2 = pFactory->CreateD2();
它按我的要求工作,看起来像互联网上的大部分示例。
但是在这个版本的抽象工厂中,如果我想在层次结构中添加更多 classes(D3、DD5、DD6...),我必须手动创建太多代码,所以我想编写更多代码通用。
//abstract factory version 2
template<typename T>
struct AbstractFactoryImpl_
{
virtual T* Create() = 0;
};
template<typename T>
struct AbstractFactory_ :public AbstractFactoryImpl_<T>
{
virtual T* Create() override
{
return new T;
}
};
template<class ...Ts>
struct ConcreteFactory_ : public AbstractFactory_<Ts>...
{
using params = std::tuple<Ts...>;
using T1 = typename std::tuple_element_t<0, params>;
using T2 = typename std::tuple_element_t<1, params>;
template<class T>
T* Create()
{
if constexpr (std::is_base_of_v<T, T1>)return AbstractFactory_<T1>::Create();
else if constexpr (std::is_base_of_v<T, T2>)return AbstractFactory_<T2>::Create();
}
};
它在客户端代码中的工作方式如下:
//version 2
//AbstractFactory* pFactory; -- can`t use because AbstractFactory in version 2 is template class.
auto pFactory_1 = new ConcreteFactory_<DD1, DD3>;
D1* pD1_ = pFactory_1->Create<D1>();
D2* pD2_ = pFactory_1->Create<D2>();
auto pFactory_2 = new ConcreteFactory_<DD2, DD4>;
pD1_ = pFactory_2->Create<D1>();
pD2_ = pFactory_2->Create<D2>();
所以它有效,但我必须创建两个不同的指针(pFactory_1
,pFactory_2
)指向 ConcreteFactory_
的不同实例。这是抽象工厂所没有想到的。
在第一个版本中,由于虚拟继承可以在 Base class 指针上调用 Create
。但这里我有模板库 class。所以我不能调用它的指针 Create()
。
所以问题是如何使指针或其他东西可能 std::any o std::variant 使在这个抽象工厂 class 设计中成为可能?我想要使用的客户端代码与第一个版本相同。
TYPE* pFactory_ = new ConcreteFactory_<DD1, DD3>;
D1* pD1_ = pFactory_->Create<D1>();
D2* pD2_ = pFactory_->Create<D2>();
auto pFactory_ = new ConcreteFactory_<DD2, DD4>;
pD1_ = pFactory_->Create<D1>();
pD2_ = pFactory_->Create<D2>();
完整代码:
[https://cppinsights.io/s/552bbe01]
您可能需要进行一些更改:
// Way to "pass" Type.
template <typename T> struct Tag{};
template <typename T>
struct AbstractFactory
{
virtual ~AbstractFactory() = default;
virtual std::unique_ptr<T> Create(Tag<T>) const = 0;
};
template <typename ... Ts>
struct AbstractFactories : virtual AbstractFactory<Ts>...
{
using AbstractFactory<Ts>::Create ...;
};
template <typename T, typename T2>
struct ConcreteFactory : virtual AbstractFactory<T>
{
std::unique_ptr<T> Create(Tag<T>) const override { return std::make_unique<T2>(); }
};
template <typename Base, typename ...Ts>
struct ConcreteFactories;
template <typename ... Ts1, typename ...Ts2>
struct ConcreteFactories<AbstractFactories <Ts1...>, Ts2...> : AbstractFactories <Ts1...>, ConcreteFactory<Ts1, Ts2>...
{
using ConcreteFactory<Ts1, Ts2>::Create ...;
};
随着使用
void Test(AbstractFactories<D1, D2>& factory)
{
std::unique_ptr<D1> d1 = factory.Create(Tag<D1>{});
std::unique_ptr<D2> d2 = factory.Create(Tag<D2>{});
// ...
}
int main()
{
ConcreteFactories<AbstractFactories<D1, D2>, DD1, DD3> factory1;
ConcreteFactories<AbstractFactories<D1, D2>, DD2, DD4> factory2;
Test(factory1);
Test(factory2);
}
一个简单的解决方案是在您的具体 classes 中添加一个别名。
此解决方案的优势在于它保留了您想要的语法:
struct B {};
struct D1 : B {};
struct DD1 : D1 {
using overrides = D1;
};
struct DD2 : D1 {
using overrides = D1;
};
struct D2 : B {};
struct DD3 : D2 {
using overrides = D2;
};
struct DD4 : D2 {
using overrides = D2;
};
然后,只需将您的 AbstractFactory_
类型更改为:
template<typename T>
struct AbstractFactoryImpl_
{
virtual std::unique_ptr<T> Create() = 0;
};
template<typename T>
struct AbstractFactory_ : AbstractFactoryImpl_<typename T::overrrides>
{
auto Create() -> std::unique_ptr<typename T::overrrides> override
{
return std::make_unique<T>();
}
};
使用它,您的代码将简单地工作:
auto pFactory1 = ConcreteFactory_<DD1, DD3>{};
std::unique_ptr<D1> pD1_ = pFactory_.Create<D1>();
std::unique_ptr<D2> pD2_ = pFactory_.Create<D2>();
auto pFactory2 = ConcreteFactory_<DD2, DD4>{};
pD1_ = pFactory_.Create<D1>();
pD2_ = pFactory_.Create<D2>();
So the question is how to make pointer or something else may be std::any o std::variant to make it possible in this Abstract Factory class design? The client code i wanna make to work is same like first version.
不要那样做。如果您将指针和继承用于多态性,我建议您不要混合使用其他实用程序。例如,如果您选择 return a std::any
,您将有两层间接寻址:any class,然后是指针。并且要实际使用指针,您必须将其转换为正确的类型,从而使基于继承的间接寻址变得无用。
如果您想 return std::any
,请完整 std::any
并始终转换为正确的类型并完全跳过继承。如果你想使用 std::variant
,删除继承并跳过虚拟的东西,只需将你所有的类型放在变体中。
看你的继承结构,确实很深(多级继承)。解决方案是让 BFactory
具有纯虚函数 return 指向 B
的指针,并使您的所有工厂都从它扩展。
它会在您的代码中添加转换,几乎无处不在。另外,你自己重复一遍,因为你必须在你的工厂中重复整个继承结构 classes.
在这一点上,我会简单地使用类型擦除来代替。我将使用泛型,具体的,而不是深度复杂的工厂层次结构,它会根据您构造它的方式改变其行为。您可以自己推出,但也可以使用标准库提供的。最简单的就是std::function
:
struct ClassThatNeedToMakeD1 {
std::function<std::unique_ptr<D1>()> makeD1;
void stuff() {
auto myD1ptr = makeD1();
}
};
int main() {
auto myClass = ClassThatNeedToMakeD1{
[]{ return std::make_unique<DD2>(); }
};
myClass.stuff();
}
这保持简单、可扩展,并且会在你想要的地方做你想做的事。不是将所有 B
子 classes 的构造策略耦合到 classes 的层次结构中,您只需在需要该类型擦除工厂函数的地方分配想要的行为。
如果你不想使用std::function
,你可以推出你自己的有限等价物:
template<typename T>
struct AnyFactory {
template<typename Function>
AnyFactory(Function f) : _factory{std::make_unique<Concrete<Function>>(std::move(f))} {}
auto operator()() const -> std::unique_ptr<T> {
return _factory->create();
}
private:
struct Abstract {
virtual ~Abstract() = default;
virtual auto create() const -> std::unique_ptr<T> = 0;
};
template<typename Function>
struct Concrete : Abstract {
Concrete(Function c) noexcept : _create{std::move(c)} {}
auto create() const -> std::unique_ptr<T> override {
return _create();
}
Function _create;
};
// Copy on write. The const is very important, don't remove it.
// If you want to remove the const, change to unique_ptr + clone function in Abstract
std::shared_ptr<const Abstract> _factory;
};
struct ClassThatNeedToMakeD1 {
AnyFactory<D1> makeD1;
void stuff() {
auto myD1ptr = makeD1();
}
};
int main() {
// Usage stays the same
auto myClass = ClassThatNeedToMakeD1{
[]{ return std::make_unique<DD2>(); }
};
myClass.stuff();
}
小建议:
- 使用
std::unique_ptr<T>
代替 T*
,使用 std::make_unique<T>()
代替 new T
。我在我的回答中应用了它。
- 使用结构时,不需要
public
继承。结构默认公开继承。
- 添加
override
关键字时,不要输入virtual
。这是多余的。
在用 C++ 进行抽象工厂的第一次实验时(在阅读现代 C++ 设计 - A. Alexandrescu - 第 9 部分时)我有一个问题。 如果 classes 层次结构如下所示:
struct B {};
struct D1 :public B {};
struct DD1 : public D1 {};
struct DD2 : public D1 {};
struct D2 :public B {};
struct DD3 : public D2 {};
struct DD4 : public D2 {};
我有这个抽象工厂代码:
//abstract factory version 1
struct AbstractFactoryImpl
{
virtual D1* CreateD1() = 0;
virtual D2* CreateD2() = 0;
};
struct AbstractFactory : public AbstractFactoryImpl
{
virtual D1* CreateD1() { return new D1; };
virtual D2* CreateD2() { return new D2; };
};
template<class ... Ts>
struct ConcreteFactory :public AbstractFactory
{
using params = std::tuple<Ts...>;
using T1 = typename std::tuple_element_t<0, params>;
using T2 = typename std::tuple_element_t<1, params>;
virtual D1* CreateD1()override
{
static_assert(std::is_base_of_v<D1, T1>);
return new T1;
};
virtual D2* CreateD2()override
{
static_assert(std::is_base_of_v<D2, T2>);
return new T2;
};
};
并像这样在客户端代码中使用它。
//version 1
AbstractFactory* pFactory;
pFactory = new ConcreteFactory<DD1, DD3>;
D1* pD1 = pFactory->CreateD1();
D2* pD2 = pFactory->CreateD2();
pFactory = new ConcreteFactory<DD2, DD4>;
pD1 = pFactory->CreateD1();
pD2 = pFactory->CreateD2();
它按我的要求工作,看起来像互联网上的大部分示例。
但是在这个版本的抽象工厂中,如果我想在层次结构中添加更多 classes(D3、DD5、DD6...),我必须手动创建太多代码,所以我想编写更多代码通用。
//abstract factory version 2
template<typename T>
struct AbstractFactoryImpl_
{
virtual T* Create() = 0;
};
template<typename T>
struct AbstractFactory_ :public AbstractFactoryImpl_<T>
{
virtual T* Create() override
{
return new T;
}
};
template<class ...Ts>
struct ConcreteFactory_ : public AbstractFactory_<Ts>...
{
using params = std::tuple<Ts...>;
using T1 = typename std::tuple_element_t<0, params>;
using T2 = typename std::tuple_element_t<1, params>;
template<class T>
T* Create()
{
if constexpr (std::is_base_of_v<T, T1>)return AbstractFactory_<T1>::Create();
else if constexpr (std::is_base_of_v<T, T2>)return AbstractFactory_<T2>::Create();
}
};
它在客户端代码中的工作方式如下:
//version 2
//AbstractFactory* pFactory; -- can`t use because AbstractFactory in version 2 is template class.
auto pFactory_1 = new ConcreteFactory_<DD1, DD3>;
D1* pD1_ = pFactory_1->Create<D1>();
D2* pD2_ = pFactory_1->Create<D2>();
auto pFactory_2 = new ConcreteFactory_<DD2, DD4>;
pD1_ = pFactory_2->Create<D1>();
pD2_ = pFactory_2->Create<D2>();
所以它有效,但我必须创建两个不同的指针(pFactory_1
,pFactory_2
)指向 ConcreteFactory_
的不同实例。这是抽象工厂所没有想到的。
在第一个版本中,由于虚拟继承可以在 Base class 指针上调用 Create
。但这里我有模板库 class。所以我不能调用它的指针 Create()
。
所以问题是如何使指针或其他东西可能 std::any o std::variant 使在这个抽象工厂 class 设计中成为可能?我想要使用的客户端代码与第一个版本相同。
TYPE* pFactory_ = new ConcreteFactory_<DD1, DD3>;
D1* pD1_ = pFactory_->Create<D1>();
D2* pD2_ = pFactory_->Create<D2>();
auto pFactory_ = new ConcreteFactory_<DD2, DD4>;
pD1_ = pFactory_->Create<D1>();
pD2_ = pFactory_->Create<D2>();
完整代码: [https://cppinsights.io/s/552bbe01]
您可能需要进行一些更改:
// Way to "pass" Type.
template <typename T> struct Tag{};
template <typename T>
struct AbstractFactory
{
virtual ~AbstractFactory() = default;
virtual std::unique_ptr<T> Create(Tag<T>) const = 0;
};
template <typename ... Ts>
struct AbstractFactories : virtual AbstractFactory<Ts>...
{
using AbstractFactory<Ts>::Create ...;
};
template <typename T, typename T2>
struct ConcreteFactory : virtual AbstractFactory<T>
{
std::unique_ptr<T> Create(Tag<T>) const override { return std::make_unique<T2>(); }
};
template <typename Base, typename ...Ts>
struct ConcreteFactories;
template <typename ... Ts1, typename ...Ts2>
struct ConcreteFactories<AbstractFactories <Ts1...>, Ts2...> : AbstractFactories <Ts1...>, ConcreteFactory<Ts1, Ts2>...
{
using ConcreteFactory<Ts1, Ts2>::Create ...;
};
随着使用
void Test(AbstractFactories<D1, D2>& factory)
{
std::unique_ptr<D1> d1 = factory.Create(Tag<D1>{});
std::unique_ptr<D2> d2 = factory.Create(Tag<D2>{});
// ...
}
int main()
{
ConcreteFactories<AbstractFactories<D1, D2>, DD1, DD3> factory1;
ConcreteFactories<AbstractFactories<D1, D2>, DD2, DD4> factory2;
Test(factory1);
Test(factory2);
}
一个简单的解决方案是在您的具体 classes 中添加一个别名。
此解决方案的优势在于它保留了您想要的语法:
struct B {};
struct D1 : B {};
struct DD1 : D1 {
using overrides = D1;
};
struct DD2 : D1 {
using overrides = D1;
};
struct D2 : B {};
struct DD3 : D2 {
using overrides = D2;
};
struct DD4 : D2 {
using overrides = D2;
};
然后,只需将您的 AbstractFactory_
类型更改为:
template<typename T>
struct AbstractFactoryImpl_
{
virtual std::unique_ptr<T> Create() = 0;
};
template<typename T>
struct AbstractFactory_ : AbstractFactoryImpl_<typename T::overrrides>
{
auto Create() -> std::unique_ptr<typename T::overrrides> override
{
return std::make_unique<T>();
}
};
使用它,您的代码将简单地工作:
auto pFactory1 = ConcreteFactory_<DD1, DD3>{};
std::unique_ptr<D1> pD1_ = pFactory_.Create<D1>();
std::unique_ptr<D2> pD2_ = pFactory_.Create<D2>();
auto pFactory2 = ConcreteFactory_<DD2, DD4>{};
pD1_ = pFactory_.Create<D1>();
pD2_ = pFactory_.Create<D2>();
So the question is how to make pointer or something else may be std::any o std::variant to make it possible in this Abstract Factory class design? The client code i wanna make to work is same like first version.
不要那样做。如果您将指针和继承用于多态性,我建议您不要混合使用其他实用程序。例如,如果您选择 return a std::any
,您将有两层间接寻址:any class,然后是指针。并且要实际使用指针,您必须将其转换为正确的类型,从而使基于继承的间接寻址变得无用。
如果您想 return std::any
,请完整 std::any
并始终转换为正确的类型并完全跳过继承。如果你想使用 std::variant
,删除继承并跳过虚拟的东西,只需将你所有的类型放在变体中。
看你的继承结构,确实很深(多级继承)。解决方案是让 BFactory
具有纯虚函数 return 指向 B
的指针,并使您的所有工厂都从它扩展。
它会在您的代码中添加转换,几乎无处不在。另外,你自己重复一遍,因为你必须在你的工厂中重复整个继承结构 classes.
在这一点上,我会简单地使用类型擦除来代替。我将使用泛型,具体的,而不是深度复杂的工厂层次结构,它会根据您构造它的方式改变其行为。您可以自己推出,但也可以使用标准库提供的。最简单的就是std::function
:
struct ClassThatNeedToMakeD1 {
std::function<std::unique_ptr<D1>()> makeD1;
void stuff() {
auto myD1ptr = makeD1();
}
};
int main() {
auto myClass = ClassThatNeedToMakeD1{
[]{ return std::make_unique<DD2>(); }
};
myClass.stuff();
}
这保持简单、可扩展,并且会在你想要的地方做你想做的事。不是将所有 B
子 classes 的构造策略耦合到 classes 的层次结构中,您只需在需要该类型擦除工厂函数的地方分配想要的行为。
如果你不想使用std::function
,你可以推出你自己的有限等价物:
template<typename T>
struct AnyFactory {
template<typename Function>
AnyFactory(Function f) : _factory{std::make_unique<Concrete<Function>>(std::move(f))} {}
auto operator()() const -> std::unique_ptr<T> {
return _factory->create();
}
private:
struct Abstract {
virtual ~Abstract() = default;
virtual auto create() const -> std::unique_ptr<T> = 0;
};
template<typename Function>
struct Concrete : Abstract {
Concrete(Function c) noexcept : _create{std::move(c)} {}
auto create() const -> std::unique_ptr<T> override {
return _create();
}
Function _create;
};
// Copy on write. The const is very important, don't remove it.
// If you want to remove the const, change to unique_ptr + clone function in Abstract
std::shared_ptr<const Abstract> _factory;
};
struct ClassThatNeedToMakeD1 {
AnyFactory<D1> makeD1;
void stuff() {
auto myD1ptr = makeD1();
}
};
int main() {
// Usage stays the same
auto myClass = ClassThatNeedToMakeD1{
[]{ return std::make_unique<DD2>(); }
};
myClass.stuff();
}
小建议:
- 使用
std::unique_ptr<T>
代替T*
,使用std::make_unique<T>()
代替new T
。我在我的回答中应用了它。 - 使用结构时,不需要
public
继承。结构默认公开继承。 - 添加
override
关键字时,不要输入virtual
。这是多余的。