简体   繁体   English

模板多态性和 unique_ptr

[英]Template polymorphism and unique_ptr

I'm currently trying to program a pipeline, which is able to process different kind of data in every pipeline element.我目前正在尝试编写一个管道,它能够处理每个管道元素中的不同类型的数据。 Now I want to use unique_ptr s with some kind of template polymorphism and template specialization.现在我想使用具有某种模板多态性和模板特化的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 { ... }
};

Now a function shall take a unique_ptr of a partial specialized PipelineElement .现在,函数应采用部分专用PipelineElementunique_ptr However, the following won't compile with the error message:但是,以下内容不会与错误消息一起编译:

auto Pipeline::setStart<int>(std::unique_ptr<PipelineElement<Start,int>,std::default_delete<PipelineElement<Start,int>>>)': cannot convert argument 1 from 'std::unique_ptr<Producer,std::default_delete<Producer>>' to 'std::unique_ptr<PipelineElement<Start,int>,std::default_delete<PipelineElement<Start,int>>> auto Pipeline::setStart<int>(std::unique_ptr<PipelineElement<Start,int>,std::default_delete<PipelineElement<Start,int>>>)':无法从 'std::unique_ptr<Producer 转换参数 1, std::default_delete<Producer>>' 到 'std::unique_ptr<PipelineElement<Start,int>,std::default_delete<PipelineElement<Start,int>>>

class Pipeline {
    template<typename Out>
    static auto setStart(std::unique_ptr<PipelineElement<Start, Out>> element) { ... }
};

int main() {
    Pipeline::setStart(std::make_unique<Producer>());
}

If I use regular pointers instead, it does compile without any errors.如果我改用常规指针,它会编译而不会出现任何错误。

Why does the version with normal pointers compile, and the version with smart pointers doesn't?为什么带有普通指针的版本可以编译,而带有智能指针的版本却不能?

class Pipeline {
    template<typename Out>
    auto setStart(PipelineElement<Start, Out>* element) { ... }
};

int main() {
    Pipeline::setStart(new Producer());
}

The PipelineElement doesn't have a virtual destructor. PipelineElement没有虚拟析构函数。 So deleting Producer by deleting pointer to PipelineElement will cause an error.所以通过删除指向PipelineElement指针来删除Producer会导致错误。

Edit: due to @RemyLebeau info.编辑:由于@RemyLebeau 信息。 Unfortunately, std::unique_ptr fails to figure this out.不幸的是, std::unique_ptr这个问题。 The compilation issue is because compiler fails to deduce the template parameters of setStart .编译问题是因为编译器无法推导出setStart的模板参数。 Note: please, in the future provide genuine error messages and not fake ones.注意:请在未来提供真正的错误信息,而不是虚假信息。

The ability to deduce template parameters is limited and doesn't try most forms of conversions except some trivial ones during the template parameters search.推断模板参数的能力是有限的,除了在模板参数搜索过程中一些微不足道的转换外,不会尝试大多数形式的转换。 If you don't want to specify the template parameters each time it is adviced to accept a more general type of input and impose SFINEA-based restrictions:如果您不想在每次建议接受更一般类型的输入并施加基于 SFINEA 的限制时都指定模板参数:

Here, I wrote an example based on restricting input unique_ptr<T> to have T inherit from PipelineElementBase .在这里,我编写了一个基于限制输入unique_ptr<T>以让TPipelineElementBase继承的示例。

#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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM