我经常写工厂,其签名类似于以下内容:
std::unique_ptr<AbstractType> createUnique(IDType id);
std::shared_ptr<AbstractType> createShared(IDType id);
前者会做类似的事情:
switch (id)
{
case kID1:
return std::make_unique<Type1>();
}
后者
switch (id)
{
case kID1:
return std::make_shared<Type1>();
}
好吧,这两个函数之间的唯一区别是使用的“make”函数(
make_shared
或 make_unique
)和返回类型(shared_ptr
或 unique_ptr
)。这会导致代码重复。
我想知道如何编写一个模板化的
create
函数,它接受指针类型和“make”函数类型并使用它们。像这样的东西:
template <typename PTR, typename MAKE>
PTR create(IDType id)
{
switch (id)
{
case kID1:
return MAKE<Type1>();
}
}
我知道上面的代码无效。
此外,我知道
std::shared_ptr
可以从 std::unique_ptr
创建,反之亦然,但我这个工厂将用于不同的应用程序,其中一个可能使用 shared_ptr
,另一个可能使用 unique_ptr
.这样,代码可重用,但对于特定用例也很有效。
我的建议,只有一个
std::unique_ptr<AbstractType>
的返回类型。如果你这样做,那么你可以使用 std::unique_ptr
和 std::shared_ptr
like 的功能
auto my_ptr = createInterface(id);
有
my_ptr
是unique_ptr
或
std::shared_ptr<AbstractType> my_ptr = createInterface(id);
现在返回的
unique_ptr
被转换成 shared_ptr
.
为了避免丢失优化内存使用的
std::make_shared
,我会这样处理这个问题:
class AbstractType {
public:
virtual ~AbstractType() = default;
virtual int f() const = 0;
};
class Foo : public AbstractType {
public:
int f() const override
{
return 1;
}
};
class Bar : public AbstractType {
public:
int f() const override
{
return 2;
}
};
enum class IDType {
Foo,
Bar,
};
class Factory {
public:
std::unique_ptr<AbstractType> createUnique(IDType id);
std::shared_ptr<AbstractType> createShared(IDType id);
private:
template <typename MakePtr>
auto createUniversal(IDType id);
};
//======================
class WrapMakeUnique {
public:
template <typename Base, typename T, typename... Args>
static auto make(Args... args) -> std::unique_ptr<Base>
{
return std::make_unique<T>(std::forward<Args>(args)...);
}
};
class WrapMakeShared {
public:
template <typename Base, typename T, typename... Args>
static auto make(Args... args) -> std::shared_ptr<Base>
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
};
template <typename MakePtr>
auto Factory::createUniversal(IDType id)
{
switch (id) {
case IDType::Foo:
return MakePtr::template make<AbstractType, Foo>();
case IDType::Bar:
return MakePtr::template make<AbstractType, Bar>();
}
throw std::invalid_argument { "IDType out of range" };
}
std::unique_ptr<AbstractType> Factory::createUnique(IDType id)
{
return createUniversal<WrapMakeUnique>(id);
}
std::shared_ptr<AbstractType> Factory::createShared(IDType id)
{
return createUniversal<WrapMakeShared>(id);
}
你的通用工厂方法非常接近
template <typename PTR, typename MAKE>
PTR create(IDType id)
{
switch (id)
{
case kID1:
return MAKE<Type1>();
}
}
您缺少的关键部分似乎是 (1)
MAKE
是一种类型,所以 MAKE<Type1>()
是我们可能想要跳过的语法,以及 (B) 如何使用它。
理想的使用方式是使用两个工厂类:
template<class Base>
struct make_unique_factory {
using PTR = std::unique_ptr<Base>;
template<class Derived, class... Args >
std::unique_ptr<Base> create(Args&&... args ) const
{return std::make_unique<Derived>(std::forward<Args>(args)...);}
};
template<class Base>
struct make_shared_factory {
using PTR = std::shared_ptr<Base>;
template<class Derived, class... Args >
std::shared_ptr<Base> create(Args&&... args ) const
{return std::make_shared<Derived>(std::forward<Args>(args)...);}
};
(我们使用类而不是方法,以便编译器更容易内联。方法参数更难内联)
然后使用类似于您所拥有的:
template <typename MAKE>
auto create(IDType id) -> typename MAKE::PTR
{
switch (id)
{
case kID1:
return MAKE{}.template create<Type1>();
}
}
std::unique_ptr<AbstractType> createUnique(IDType id) {
return create<make_unique_factory<AbstractType>>(id);
}
std::shared_ptr<AbstractType> createShared(IDType id) {
return create<make_shared_factory<AbstractType>>(id);
}
http://coliru.stacked-crooked.com/a/bc3d5d2431a0b0da
你遇到麻烦的原因可能是这条线
MAKE{}.template create<Type1>()
所以我会把它分解。 MAKE{}
构造 MAKE
类的一个实例。然后我们在该实例上调用 create<Type1>()
方法。但是,编译器在编译这个create
方法的时候,还不知道MAKE
是什么,所以不知道它的create
方法是一个模板方法。所以烦人的是我们必须明确地告诉它这个方法是一个模板方法,导致MAKE{}.template create<Type1>()
.