我目前正在尝试编程一个管道,它能够在每个管道元素中处理不同类型的数据。现在我想使用具有某种模板多态性和模板专门化的unique_ptr
。
struct Start {}; // Dummy struct
template<typename In, typename Out>
class PipelineElement {
public:
virtual Out process(In in) = 0;
};
// partial template specialization for the first pipeline element
template<typename Out>
class PipelineElement<Start, Out> {
public:
virtual Out process() = 0;
};
class Producer : public PipelineElement<Start, int> {
int process() override { ... }
};
现在,一个函数将取部分专用PipelineElement
的unique_ptr
。然而,以下内容不会编译并显示错误消息:
自动管道::setStart<int>(std::unique_ptr<PipelineElement<Start,int>,std::default_delete<PipelineElement<Start,int>>(':无法从'std::unique_ptr<转换参数1;Producer,std::default_delete<制作人>gt;'到'std::unique_ptr<PipelineElement<开始,int>,std::default_delete<PipelineElement<开始,int>gt>
class Pipeline {
template<typename Out>
static auto setStart(std::unique_ptr<PipelineElement<Start, Out>> element) { ... }
};
int main() {
Pipeline::setStart(std::make_unique<Producer>());
}
如果我改为使用常规指针,它编译时不会出现任何错误。
为什么带有普通指针的版本会编译,而带有智能指针的版本不会?
class Pipeline {
template<typename Out>
auto setStart(PipelineElement<Start, Out>* element) { ... }
};
int main() {
Pipeline::setStart(new Producer());
}
PipelineElement
没有虚拟析构函数。因此,通过删除指向PipelineElement
的指针来删除Producer
将导致错误。
编辑:由于@RemyLebeau信息。不幸的是,std::unique_ptr
未能解决这一问题。编译问题是因为编译器无法推导出setStart
的模板参数。注意:请以后提供真实的错误消息,而不是伪造的错误消息。
推导模板参数的能力是有限的,并且在模板参数搜索过程中,除了一些琐碎的转换外,不会尝试大多数形式的转换。如果你不想每次都指定模板参数,建议接受更通用的输入类型并施加基于SFINEA的限制:
在这里,我写了一个基于限制输入unique_ptr<T>
使T
继承PipelineElementBase
的示例。
#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;
struct Start {}; // Dummy struct
class PipelineElementBase
{
public:
virtual ~PipelineElementBase() = default;
};
template<typename In, typename Out>
class PipelineElement : public PipelineElementBase
{
public:
~PipelineElement() { cout << "~PipelineElement<In,Out>" << endl; }
virtual Out process(In in) = 0;
};
// partial template specialization for the first pipeline element
template<typename OutParam>
class PipelineElement<Start, OutParam> : public PipelineElementBase
{
public:
using Out = OutParam;
~PipelineElement() { cout << "~PipelineElement<Start,Out>" << endl; }
virtual Out process() = 0;
};
class Producer : public PipelineElement<Start, int>
{
public:
~Producer() { cout << "~Producer" << endl; }
int process() override { return 1; }
};
class Pipeline
{
public:
template<typename PE, std::enable_if_t<std::is_base_of_v<PipelineElementBase, PE>,int> = 0>
static auto setStart(std::unique_ptr<PE> element)
{
using Out = typename PE::Out;
return 1;
}
};
int main()
{
Pipeline::setStart(std::make_unique<Producer>());
return 0;
}