是否有更好/安全的方法将shared_ptr的非const引用强制转换为基类?

时间:2016-06-07 22:39:38

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

如果你有一个带有虚方法的类Base和一个实现虚方法的类实现,有没有办法强制转换std :: shared_ptr<实施> &安培;到std :: shared<基数> &放大器;?编译器允许这个用于const引用,但是对于非const引用,它会失败,例如"案例A"在下面的代码中。有一个简单的方法吗?

如果没有,我的解决方法有多安全" questionable_cast"案例B?

#include <iostream>
#include <memory>

class Base
{
public:
    virtual void set_value(int x) = 0;
};

class Implementation : public Base
{
public:
    Implementation() : m_value(0) { }
    void set_value(int x) override
    {
    m_value = x;
    }
    int get_value() const
    {
    return m_value;
    }
private:
    int m_value;
};


void do_something(std::shared_ptr<Base>& base)
{
    base->set_value(5);

    /// Code like this makes the non-const argument necessary
    base = std::make_shared<Implementation>();
}

template <class T, class U>
std::shared_ptr<T>& questionable_cast(std::shared_ptr<U>& u)
{
    /// This code is here to assure the cast is allowed
    std::shared_ptr<T> tmp = u;
    (void)tmp;

    return *reinterpret_cast<std::shared_ptr<T>*>(&u);
}

int main()
{
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>();

    // The following line causes a compiler error:
    //  invalid initialization of reference of type ‘std::shared_ptr<Base>&’ ...
    // do_something(a);
    // do_something(std::dynamic_pointer_cast<Base>(a));

    // This is the workaround
    do_something(questionable_cast<Base>(a));

    std::cerr << "a = " << a->get_value() << std::endl;

    return 0;
}

1 个答案:

答案 0 :(得分:2)

最初提出的问题的两个明显的解决方案:1。使do_something对shared_ptr(或按值的shared_ptr)进行const引用。 2.创建一个名为shared_ptr并传递对它的引用:例如

int main()
{
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>();
    std::shared_ptr<Base> b = a;  // This conversion works.
    do_something(b);  // Pass a reference to b instead.
    return 0;
}

您的questionable_cast函数违反了严格别名规则,并调用了未定义的行为。它很可能在初始测试中工作,然后编译器的新版本将优化提升一个档次,并且在演示期间它将失败。

处理do_something更改指针的情况:

int main()
{
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>();
    std::shared_ptr<Base> b = a;  // This conversion works.
    do_something(b);  // Pass a reference to b instead.
    const auto aa = std::dynamic_pointer_cast<Implementation>(b);
    if (aa)
        a = aa;
    else
        ; // Handle the error here
    return 0;
}

如果do_something保证返回相同派生类型的指针,即使它没有返回相同的指针,也将它包装在模板函数中:

template <typename T>
void do_something_ex( std::shared_ptr<T>& a )
{
    std::shared_ptr<Base> b = a;
    do_something(b)
    a = std::dynamic_pointer_cast<T>(b);
    if (!a)
        throw_or_assert;
}