如何将std :: unique_ptr传递给函数


101

如何将a传递std::unique_ptr给函数?可以说我有以下课程:

class A
{
public:
    A(int val)
    {
        _val = val;
    }

    int GetVal() { return _val; }
private:
    int _val;
};

以下内容无法编译:

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);

    return 0;
}

为什么不能将a传递std::unique_ptr给函数?当然这是构建的主要目的吗?还是C ++委员会打算让我退回到原始C风格的指针并像这样传递它:

MyFunc(&(*ptr)); 

最奇怪的是,为什么这是一种好的传递方式?似乎非常不一致:

MyFunc(unique_ptr<A>(new A(1234)));

7
只要不属于原始指针,就可以“回退”到原始C样式指针没有任何问题。您可能更喜欢编写“ ptr.get()”。虽然,如果您不需要可为空性,则首选引用。
克里斯·德鲁

Answers:


148

这里基本上有两个选择:

通过引用传递智能指针

void MyFunc(unique_ptr<A> & arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
}

将智能指针移到函数参数中

请注意,在这种情况下,断言将成立!

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(move(ptr));
    assert(ptr == nullptr)
}

@VermillionAzure:您所指的功能通常称为不可复制的,不是唯一的。
比尔·林奇

1
谢谢。在这种情况下,我想创建一个实例,对其执行一些操作,然后将所有权转让给其他人,move()似乎非常适合。
user3690202

7
unique_ptr当函数可能会或可能不会从中移动时,才应传递一个按引用。然后它应该是右值引用。要观察某个对象而不需要有关其所有权语义的任何信息,请使用诸如A const&或的引用A&
Potatoswatter

14
收听Herb Sutters在CppCon 2014上的演讲。他强烈不鼓励将引用传递给unique_ptr <>。本质是,在处理所有权时,应仅使用诸如unique_ptr <>或shared_ptr <>之类的智能指针。参见www.youtube.com/watch?v=xnqTKD8uD64在这里您可以找到幻灯片 github.com/CppCon/CppCon2014/tree/master/Presentations/…– schorsch_76 2015
6

2
@Furihr:这只是一种显示ptr举动后的价值的方式。
比尔·林奇

28

您正在按值传递它,这意味着要进行复制。那不是很独特,是吗?

您可以移动值,但这意味着将对象的所有权及其对生存期的控制权传递给函数。

如果保证对象的生存期在对MyFunc的调用的生存期内,只需通过传递原始指针ptr.get()


17

为什么不能将a传递unique_ptr给函数?

您不能执行此操作,因为unique_ptr具有移动构造函数,但没有副本构造函数。根据标准,当定义了移动构造函数但未定义复制构造函数时,将删除该复制构造函数。

12.8复制和移动类对象

...

7如果类定义未显式声明一个副本构造函数,则隐式声明一个副本构造函数。如果类定义声明了move构造函数或move赋值运算符,则隐式声明的copy构造函数将定义为delete;

您可以unique_ptr使用以下方法将传递给函数:

void MyFunc(std::unique_ptr<A>& arg)
{
    cout << arg->GetVal() << endl;
}

并像使用它一样使用它:

要么

void MyFunc(std::unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

并像这样使用它:

std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));

重要的提示

请注意,如果使用第二种方法,ptr则在调用return之后没有指针的所有权std::move(ptr)

void MyFunc(std::unique_ptr<A>&& arg)void MyFunc(std::unique_ptr<A>& arg)由于两者都是引用,因此具有相同的效果。

在第一种情况下,ptr在调用之后,该指针仍具有所有权MyFunc


5

由于MyFunc不拥有所有权,因此最好拥有:

void MyFunc(const A* arg)
{
    assert(arg != nullptr); // or throw ?
    cout << arg->GetVal() << endl;
}

或更好

void MyFunc(const A& arg)
{
    cout << arg.GetVal() << endl;
}

如果您确实想拥有所有权,则必须移动资源:

std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));

或直接传递r值参考:

MyFunc(std::make_unique<A>(1234));

std::unique_ptr 并没有副本来保证只有一个所有者。


该答案缺少您前两种情况下对MyFunc的调用的外观。不确定是使用ptr.get()还是只是将传递ptr给函数?
泰勒斯汀

MyFunc传递r值引用的预期签名是什么?
詹姆斯·希尔斯霍恩

@JamesHirschorn:void MyFunc(A&& arg)以r值作为参考...
Jarod42 '20

@ Jarod42这就是我的猜测,但是使用时typedef int A[]MyFunc(std::make_unique<A>(N))会出现编译器错误:错误:类型'std :: _ MakeUniq <int []> :: __ array'的表达式对类型'int(&&)[]'的引用的初始化无效aka'std :: unique_ptr <int [],std :: default_delete <int []>>'}g++ -std=gnu++11最近够用吗?
James Hirschorn

@ Jarod42我确认使用ideone的C ++ 14失败:ideone.com/YRRT24
James

4

为什么不能将a传递unique_ptr给函数?

您可以但不能复制-因为std::unique_ptr<>它不可复制。

当然这是构建的主要目的吗?

除其他事项外,std::unique_ptr<>旨在明确标记 唯一所有权(与相对std::shared_ptr<>)。

最奇怪的是,为什么这是一种好的传递方式?

因为在这种情况下,没有复制构造。


感谢您的回答。只是出于兴趣,您能解释一下为什么最后一种传递方法没有使用复制构造函数吗?我会认为,使用unique_ptr的构造函数会在堆栈上生成一个实例,该实例将使用复制构造函数复制到MyFunc()的参数中吗?尽管我承认我在这方面的回忆有些模糊。
user3690202

2
由于它是一个右值,因此将调用move-constructor。尽管您的编译器肯定会对此进行优化。
Nielk

0

因为unique_ptr是唯一所有权,所以如果您要将其作为参数传递,请尝试

MyFunc(move(ptr));

但在那之后的状态ptrmainnullptr


4
“将是未定义的”-不,它将为null,否则unique_ptr将毫无用处。
TC

0

std::unique_ptr<T>作为值传递给函数不起作用,因为正如您提到的那样,unique_ptr不可复制。

那这个呢?

std::unique_ptr<T> getSomething()
{
   auto ptr = std::make_unique<T>();
   return ptr;
}

该代码有效


如果该代码有效,那么我的猜测是它不是在复制unique_ptr,而是实际上使用move语义移动它。
user3690202 '20

我猜可能是返回值优化(RVO)
Noueman Khalikine
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.