繁体   English   中英

转换表单boost :: shared_ptr时出错 <T> 到std :: shared_ptr <T>

[英]Error converting form boost::shared_ptr<T> to std::shared_ptr<T>

我写了一个函数模板,通过遵循这个提议boost::shared_ptr<T> “转换”/重新打包到std::shared_ptr<T> ,反之亦然。 它的正常工作,除非我有一个boost::shared_pt<T>和类型T是一个抽象类。

到目前为止我想到的是,当boost/shared_ptr.hppboost/shared_array.hpp一起包含时会出现问题。 如果仅包含boost/shared_ptr.hpp则当T的类型是抽象类时,它正在工作。

我正在使用clang 3.3并提升1.55.0。 如果有人可以告诉我为什么它不起作用以及如何让它工作,那将是很棒的。

谢谢你的帮助



这是一个最小的例子:

//main.cpp

#include <boost/shared_array.hpp> //removing this include and it's working
#include <boost/shared_ptr.hpp>
#include <memory>

template<typename SharedPointer> struct Holder {

    SharedPointer p;

    Holder(const SharedPointer &p) : p(p) {}
    Holder(const Holder &other) : p(other.p) {}
    Holder(Holder &&other) : p(std::move(other.p)) {}

    void operator () (...) const {}
};


template<class T>
std::shared_ptr<T> to_std_ptr(const boost::shared_ptr<T> &p)
{
    typedef Holder<std::shared_ptr<T>> H;

    if(H *h = boost::get_deleter<H, T>(p))  // get_deleter seems to cause the problem
    {
        return h->p;
    }
    else
    {
        return std::shared_ptr<T>(p.get(), Holder<boost::shared_ptr<T>>(p));
    }
}



这里我用来测试它的代码:

//main.cpp

template<typename T> class Base 
{
public:
    T value;   
    virtual void abstract() = 0;
    virtual ~Base() {}
};

template<typename T> class Derived : public Base<T>
{
public:    
    virtual void abstract() override {}
    virtual ~Derived() {}
};


int main(int argc, const char * argv[])
{
    boost::shared_ptr<Base<int>> ptr{new Derived<int>()};

    // error here
    std::shared_ptr<Base<int>> a = to_std_ptr(ptr);

    // no error here
    std::shared_ptr<Base<int>> b = to_std_ptr(boost::static_pointer_cast<Derived<int>>(ptr));

    return 0;
}



这是错误消息(缩写):

boost/smart_ptr/shared_array.hpp:111:102: error: array of abstract class type 'Base<int>'
    shared_array( shared_array<Y> const & r, typename boost::detail::sp_enable_if_convertible< Y[], T[] >::type = boost::detail::sp_empty() )

main.cpp:64:40: note: in instantiation of template class 'boost::shared_array<Base<int> >' requested here 
    if(H *h = boost::get_deleter<H, T>(p))

main.cpp:86:36: note: in instantiation of function template specialization 'to_std_ptr<Base<int> >'requested here
    std::shared_ptr<Base<int>> i = to_std_ptr(ptr);

main.cpp:23:18: note: unimplemented pure virtual method 'abstract' in 'Base'
    virtual void abstract() = 0;

我从错误消息中得到的是编译器试图创建一个抽象类数组当然不起作用。 但是为什么他甚至试图这样做以及什么boost/sharred_array与之相关。 他是否可能为boost::get_deleter选择了错误的重载?

以下是错误来源的一个小例子:

struct abstract
{
    virtual void foo() = 0;
};

template<class X, class Y>
struct templ {};

template<class T>
struct bar
{
    template<class U>
    bar(templ<U[], T[]>) {} // (A)
};

int main()
{
    bar<abstract> x;
}

(A)形成[abstract-type]的类型数组似乎是非法的,因此使用参数abstract 实例化类模板bar会使程序格式错误。


shared_array的背景中也发生了同样的事情。 但为什么shared_array<Base>实例化?

自由函数boost::get_deleter被重载, shared_array.hpp为重载集添加了一个重载(实际上,添加了一个模板):

template< class D, class T > D * get_deleter( shared_array<T> const & p );

在重载解析之前,甚至在找出哪些函数可行之前,需要实例化函数模板。 实例化上面的get_deleter模板会导致实例化shared_array<Base> ,这会导致程序get_deleter

解决的办法是,不要让上面的实例化:不提供模板参数T ,也不能推断出Tshared_array<T>shared_ptr<T>这两种类型无关。

更改

if(H *h = boost::get_deleter<H, T>(p))

if(H *h = boost::get_deleter<H>(p))

它的工作原理。


解释为什么让T推导起作用:

在编写函数调用时,可以使用函数模板(查看名称),必须设置模板参数。 您可以显式提供它们(在<>如在get_deleter<H, T> )。 如果没有显式地提供所有这些(如在get_deleter<H> ),则必须从函数调用的参数中推导出其余的。

在显式设置或推导出所有模板参数后,将替换它们在函数模板中的出现次数。 在此步骤中使用get_deleter<H, Derived>时出错:替换的get_deleter如下所示:

template<> H * get_deleter( shared_array<Derived> const & p );

这里,需要实例化shared_array<Derived> 但在此实例化过程中,会出现上述错误。 (注意:它不在get_deleter的直接上下文中,因此SFINAE不适用。)

现在,当您没有明确提供第二个模板参数时,必须推导出它。 并且这个演绎在功能模板中失败了

template< class D, class T > D * get_deleter( shared_array<T> const & p );

如果使用shared_ptr<Derived>类型的参数表达式。 由于演绎失败,因此不会对函数模板进行实例化,因此不会对shared_array<Derived>实例化(演绎失败=无法设置某些模板参数)。

为什么演绎失败? 编译器需要从参数表达式中推导出函数参数类型shared_array<T> const&的模板参数T ,该表达式的类型为shared_ptr<Derived> 但是,当函数参数类型可以等于参数表达式类型时,这种推论只能成功(少数例外)。 即,只有存在某种类型X才能成功,这样shared_array<X>shared_ptr<Derived>类型相同。 但是没有: shared_ptrshared_array是不相关的。

因此,不能推导出此get_deleter重载的模板参数T ; 因此,此函数模板未实例化,并且未实例化shared_array<Derived>

扣除失败是一种特殊的失败(如SFINAE):它不会导致程序格式错误(即它不会导致编译错误)。 相反,演绎没有成功的函数模板根本不会向重载集添加函数。 如果重载集中还有其他函数,则可以调用其中一个函数。

另一个功能模板

template<class D, class T> D * get_deleter( shared_ptr<T> const & p )

来自boost/smart_ptr/shared_ptr.hpp运行相同的过程。 但是,此处的推论成功,并且特殊化get_deleter<H, T> (来自to_std_ptrHT )被添加到重载集。 稍后将通过重载决策来选择它。

暂无
暂无

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

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