在C++中,unique_ptr之间的转换

3

我有下面这个C++11语法结构:

#include <vector>
#include <memory>

class X {
public:
        void f(void) { }
};

class Y : public X {
public:
        void g(void) { }
};

class A {
private:
        std::vector <std::unique_ptr <X> > xs;
public:
        void addX(std::unique_ptr <X> &ref) {
                xs.push_back(std::move(ref));
        }
};

int main() {
        A a;

        std::unique_ptr <Y> y(new Y());
        y->f();
        y->g();

        std::unique_ptr <X> x(y.release());
        a.addX(x);

        return 0;
}

在主函数中,我试图构建一个类型为 Y 的对象,然后将其添加到 a 的 X 对象向量中。但是,我不能直接使用 a.addX(y),因为 std::unique_ptr <Y> 无法转换为 std::unique_ptr <X>&。这就是我想出的解决方法,即使用已释放的 y 内部指针初始化另一个唯一指针 x。
虽然这样可以实现,但似乎不是最好的解决方案。是否有更好的方法将类型为 std::unique_ptr<Y> 的对象作为类型为 std::unique_ptr<X>& 的参数传递?
谢谢, - Stan

3
void addX(std::unique_ptr &ref) { xs.push_back(std::move(ref)); }这是非常危险的代码。它将拥有权转移给了 xs 容器,导致原始对象被析构并且指针被悬挂。 - Simple
1
void addX(std::unique_ptr <X> ref) { xs.push_back(std::move(ref)); } 是非邪恶版本。 - Yakk - Adam Nevraumont
2个回答

5

std::unique_ptr已经提供了正确的重载,但是你需要使用std::move将它们用作unique_ptr,因为unique_ptr不可复制:

std::unique_ptr<X> x;
std::unique_ptr<Y> y { new Y };
x = std::move(y);

对于这种非常具体的问题,如果你需要通过引用接收子类的 unique_ptr,则需要使用模板函数,无法进行强制转换。

template < typename T, typename = typename std::enable_if< std::is_base_of<X,T>::value>::type >
void foo( std::unique_ptr<T> & ) {
}

最后,由于想要获取指针的所有权,如果您通过右值引用传递unique_ptr,则可以按照您想要的方式进行操作。

void bar( std::unique_ptr<X> && ) {
}
// then
bar( std::move(y) );

所以,实际上,OP 可以只写 a.addX(std::move(y)); 对吗? - rodrigo
好的,这看起来更好了,但仍然涉及创建对象x,这似乎是浪费的。有任何想法为什么a.addX(std::move(y))不起作用? - stanm
注意:a.addX(std::move(y)) 会导致错误: unique.cpp:31:32: error: no matching function for call to ‘A::addX(std::remove_reference<std::unique_ptr<Y>&>::type)’ - stanm
@stanm 你认为创建一个 unique_ptr 的成本有多高? - Yakk - Adam Nevraumont
如果你问这个问题是因为传递引用,那是为了避免复制...但当然对于唯一指针来说是不允许的,所以我不应该担心它。这篇文章让我感到困惑。我昨天刚接触唯一指针,所以我还在学习中。 - stanm
2
@stanm 不要使用 & 来获取 unique_ptr。你可以通过值、const&(尽管在这种情况下,通常只需使用原始指针)或者 && 来获取它(我通常会建议直接通过值来获取,而不是 &&,因为复制 unique_ptr 的成本很低,而复制的 unique_ptr 意味着你的数据总是被获取,而 && 则意味着它只有可能被获取,这会导致调用方混淆)。 - Yakk - Adam Nevraumont

2
您可以使用右值引用,并使用std::move进行委派:
#include <vector>
#include <memory>

class X {
public:
        void f(void) { }
};

class Y : public X {
public:
        void g(void) { }
};

class A {
private:
        std::vector <std::unique_ptr<X> > xs;
public:
        void addX(std::unique_ptr <X>&& ref) {
                xs.push_back(std::move(ref));
        }
};

int main() {
        std::unique_ptr <Y> y(new Y());
        y->f();
        y->g();

        A a;
        a.addX(std::move(y));

        // error: no matching function for call to ‘A::addX(std::unique_ptr<Y>&)’
        // a.addX(y);

        return 0;
}

您的函数 void addX(std::unique_ptr <X> &ref) 在内部窃取内容可能是一个值得怀疑的设计。

抱歉,显然我的表述不够清晰,但我想将其保留在A类中;我给出的代码是更复杂程序的简化版本。我的问题在于原帖的最后一段。 - stanm
2
以上的代码虽然声称使用了“通用引用”的技术,但实际上并没有使用。该代码使用了相关(在语法和概念上都有关联)的方法——右值引用。 - Yakk - Adam Nevraumont
1
转发引用和通用引用仅适用于模板函数,这里您仅使用了一个纯右值引用,并且使用std::move比std::forward更冗长。 - galop1n
@galop1n请看一下https://dev59.com/wWkw5IYBdhLWcg3wwNN5 - user2249683
@DieterLücking请再次阅读您在评论中提出的问题的答案。“std::forward仅有一个用例:将模板化的函数参数(在函数内部)转换为调用者用于传递它的值类别(左值或右值)。addX不是模板,因此在这里std::forward毫无意义!” - galop1n
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接