将表单boost :: shared_ptr <t>转换为std :: shared_ptr <t> </t> </t>时出错

时间:2014-03-05 20:21:25

标签: c++ boost c++11 shared-ptr

我写了一个函数模板,通过跟随this proposalboost::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选择了错误的重载?

1 个答案:

答案 0 :(得分:2)

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

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] 的类型数组似乎是违法的,因此实例化类模板{{1}使用参数bar使程序格式不正确。


同样的事情发生在abstract的背景中。但为什么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>,它不能从{{1}中推导T中的T }:这两种类型是无关的。

更改

shared_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 ); 的直接背景下,因此SFINAE不适用。)

现在,当您没有明确提供第二个模板参数时,必须推导出它。对于功能模板

,这种推论失败了
shared_array<Derived>

如果使用类型为get_deleter的参数表达式。由于演绎失败,因此没有实例化函数模板,因此没有template< class D, class T > D * get_deleter( shared_array<T> const & p ); 的实例化(演绎失败=无法设置某些模板参数)。

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

因此,无法推断出此shared_ptr重载的模板参数shared_array;因此,此函数模板未实例化,T未实例化。

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

另一个功能模板

get_deleter
来自shared_array<Derived>

运行相同的过程。但是,此处的扣除成功,并且专门化template<class D, class T> D * get_deleter( shared_ptr<T> const & p ) (来自boost/smart_ptr/shared_ptr.hpp的{​​{1}}和get_deleter<H, T>)会添加到重载集中。稍后将通过重载决策来选择它。