【发布时间】:2021-05-06 00:33:45
【问题描述】:
我努力一步一步地编写示例,使其清晰易懂。
这里显示的对象 BaseCreator 公开了一个创建函数,该函数使用内部类 NewObject 来分配类型 T 的新对象。类 NewObject 默认方法是使用 new 运算符的常用方法,但可以使用专门化来更改它以使用不同的过程。我们稍后会看到。
template <typename F>
class BaseCreator
{
public:
BaseCreator() = default;
virtual ~BaseCreator() = default;
// create function
template <typename T, typename... Args>
std::shared_ptr<T> create(Args&&... parameters)
{
return std::shared_ptr<T>(NewObject<T, Args...>().get(std::forward<Args>(parameters)...));
}
private:
// creator object with default functionality to create a new object
template <typename T, typename... Args>
struct NewObject
{
T* get(Args&&... parameters)
{
return new T(std::forward<Args>(parameters)...);
}
};
};
这很好用,例如假设我们有以下对象类型:
struct A
{
A(int i_i, const std::string& i_s) : i(i_i), s(i_s) {}
void addVal(int i_i) { i+=i_i; }
int i = 0;
std::string s;
};
struct B
{
B(const std::vector<float>& i_v) : v(i_v) {}
void addVal(int i_i) { v.push_back((float)i_i); }
std::vector<float> v;
};
我们可以轻松创建它们,例如:
// declare type for a creator identity
struct Group111 {};
// creator object
BaseCreator<Group111> cCreator111;
// creating A and B
std::shared_ptr<A> spA = cCreator111.create<A>(5, "Hello");
std::shared_ptr<B> spB = cCreator111.create<B>(std::vector<float>({ 0.5f,0.1f,0.7f,0.9f }));
我们还可以为特定对象和特定类型的创建者声明特定的创建方法。这里例如使用 Group222 的创建者创建对象 A 的特化:
// declare type for a creator identity
struct Group222 {};
// specialize NewObject for BaseCreator<Group222> for creating A
template<>
template< typename... Args>
struct BaseCreator<Group222>::NewObject<A, Args...>
{
A* get(int i_factor, int i_i, const std::string& i_s)
{
A* p = new A(i_i, i_s);
p->i *= i_factor;
return p;
}
};
使用 Group222 类型的创建者创建对象 A 的方法与默认方法不同。
// creator object
BaseCreator<Group222> cCreator222;
// creating A with creator of Group222 needs now additional input argument
std::shared_ptr<A> spA = cCreator222.create<A>(3, 5, "Hello");
// creating A with creator of Group222 is the same as creating using creator cCreator111
std::shared_ptr<B> spB = cCreator222.create<B>(std::vector<float>({ 0.5f,0.1f,0.7f,0.9f }));
现在解决问题:)
现在,我想派生一个新的 BaseCreator 以具有以下功能,如您所见,它具有代理创建函数并调用其基类之一,并为包添加额外的整数:
class Creator333 : public BaseCreator<Creator333>
{
public:
Creator333() = default;
virtual ~Creator333() = default;
// creator
template <typename T, typename... Args>
std::shared_ptr<T> create(Args&&... parameters)
{
int adder = 5; // just for the example
return std::shared_ptr<T>(BaseCreator<Creator333>::create<T>(adder, std::forward<Args>(parameters)...));
}
};
它带有专门的BaseCreator<Creator333>::NewObject,它应该为任何对象处理这个额外的整数(在这个例子中 - 假设对象有一个名为 addVal() 的成员函数)
template<>
template<typename T, typename... Args>
struct BaseCreator<Creator333>::NewObject<T, Args...>
{
T* get(int i_adder, Args&&... parameters)
{
T* p = new T(std::forward<Args>(parameters)...);
p->addVal(i_adder);
return p;
}
};
如果我要尝试创建一个对象,让我们说 B 使用这个 Creator333:
Creator333 cCreator333;
// creation of B using Creator333
std::shared_ptr<B> spB3 = cCreator333.create<B>(std::vector<float>({ 0.1f,0.2f}));
我会得到一个编译器错误:
error C2660: 'BaseCreator<Creator333>::NewObject<T,int &,_Ty>::get': function does not take 2 arguments
1> with
1> [
1> T=B,
1> _Ty=std::vector<float,std::allocator<float>>
1> ]
但这很奇怪,因为函数确实应该得到两个参数,如下所示:
-
Create333::create- 得到一个包含一个元素的包 std::vector({ 0.1f,0.2f}) -
它用额外的整数调用它的基本类
BaseCreator<Create333>::create,因此BaseCreator<Create333>::create得到一个包含两个元素的包:int(5) 和 std::vector({ 0.1f,0.2f}) -
BaseCreator<Create333>::create`` callsBaseCreator::NewObject.get``` 包的第一个元素用于 get(adder,包的另一个元素用于 get(Args ...
那么问题是什么以及如何解决呢?
谢谢
【问题讨论】:
-
问题在于重载解决方案并没有像您认为的那样工作,或者,也许更重要的是,您认为存在重载解决方案,但看起来并没有(或反之亦然)。我不认为这个设计会像显示的那样工作,但我只是看了一眼。看起来是一个非常熟悉的问题。我确定这个问题是重复的。谷歌搜索类似重载解析和模板参数包之类的东西。
-
感谢@Kubahasn'tforgottenMonica,您的建议有所帮助 - 请参阅下面我的回答,但如果您了解原因,它将使其变得完美。
标签: c++ c++11 variadic-templates template-specialization