c++unique_ptr参数传递
c++ unique_ptr argument passing
假设我有以下代码:
class B { /* */ };
class A {
vector<B*> vb;
public:
void add(B* b) { vb.push_back(b); }
};
int main() {
A a;
B* b(new B());
a.add(b);
}
假设在这种情况下,所有原始指针B*
都可以通过unique_ptr<B>
进行处理。
令人惊讶的是,我无法找到如何使用unique_ptr
转换此代码。经过几次尝试,我得到了以下代码,它可以编译:
class A {
vector<unique_ptr<B>> vb;
public:
void add(unique_ptr<B> b) { vb.push_back(move(b)); }
};
int main() {
A a;
unique_ptr<B> b(new B());
a.add(move(b));
}
因此,我的简单问题是:这是实现它的方法吗?特别是,move(b)
是实现这一点的唯一方法吗?(我在想右价参考,但我不完全理解它们。)
如果你有我找不到的关于移动语义、unique_ptr
等的完整解释的链接,请毫不犹豫地分享。
编辑依据http://thbecker.net/articles/rvalue_references/section_01.html,我的代码似乎还可以。
事实上,std::move只是语法糖。对于类x的对象x,move(x)
与完全相同
static_cast <X&&>(x)
这两个移动功能是必需的,因为铸造到右值参考:
- 阻止函数"add"传递值
- 使
push_back
使用B的默认移动构造函数
显然,如果我将"add"函数更改为通过引用传递(普通左值引用),我就不需要main()
中的第二个std::move
。
不过,我想确认一下这一切。。。
我有点惊讶,这里没有非常清楚和明确地回答这个问题,在我容易偶然发现的任何地方也没有。虽然我对这个东西还很陌生,但我认为可以说以下几点。
这种情况是一个调用函数,它构建了一个unique_ptr<T>
值(可能是通过将调用的结果强制转换为new
),并希望将其传递给某个将获得所指向对象所有权的函数(例如,通过将其存储在数据结构中,就像这里发生的那样,将其存储到vector
中)。为了指示调用者已经获得所有权,并且准备放弃所有权,传递unique_ptr<T>
值是适当的。在我看来,传递这样一个值有三种合理的方式。
- 按值传递,如问题中的
add(unique_ptr<B> b)
- 通过非
const
左值引用,如add(unique_ptr<B>& b)
- 通过右值引用传递,如
add(unique_ptr<B>&& b)
传递const
左值引用是不合理的,因为它不允许被调用的函数获得所有权(而const
右值引用甚至更愚蠢;我甚至不确定它是否被允许)。
就有效代码而言,选项1和3几乎是等效的:它们强制调用方编写一个右值作为调用的参数,可能是通过在对std::move
的调用中包装一个变量(如果该参数已经是右值,即在new
的结果的强制转换中未命名,则没有必要)。然而,在选项2中,传递右值(可能来自std::move
)是不允许的,并且必须使用命名的unique_ptr<T>
变量调用函数(当从new
传递强制转换时,必须首先为变量赋值)。
当确实使用了std::move
时,在调用程序中保持unique_ptr<T>
值的变量在概念上被取消引用(转换为右值,分别转换为右值引用),此时所有权被放弃。在选项1。取消引用是真实的,并且值被移动到一个临时的,该临时的值被传递给被调用函数(如果被调用函数检查调用方中的变量,它会发现它已经包含了一个空指针)。所有权已经转移,调用方不可能决定不接受它(不处理参数会导致指向的值在函数出口处被销毁;对参数调用release
方法可以防止这种情况,但只会导致内存泄漏)。令人惊讶的是,选项2。和3。在函数调用期间在语义上是等效的,尽管它们需要调用方使用不同的语法。如果被调用的函数将参数传递给另一个采用右值的函数(如push_back
方法),则在这两种情况下都必须插入std::move
,这将在此时转移所有权。如果被调用的函数忘记了对参数执行任何操作,那么调用方将发现自己仍然拥有该对象(如果为其保留了名称)(在选项2中是必须的);尽管在情况3中,函数原型要求调用方同意释放所有权(通过调用std::move
或提供临时)。总之,的方法
- 强制调用者放弃所有权,并确保实际声明所有权
- 强制调用者拥有所有权,并准备(通过提供非
const
引用)放弃它;然而,这并不是明确的(不需要甚至不允许调用std::move
),也不能保证剥夺所有权。我认为这种方法的意图相当不明确,除非它明确表示是否拥有所有权是由被调用函数决定的(可以想象一些用途,但调用方需要知道) - 强制调用方明确表示放弃所有权,如1中所示。(但所有权的实际转移被推迟到函数调用之后)
备选方案3的意图相当明确;如果真的取得了所有权,对我来说这是最好的解决方案。它的效率略高于1,因为没有指针值移动到临时值(对std::move
的调用实际上只是强制转换,不需要任何开销);如果指针在其内容被实际移动之前通过了几个中间函数,那么这可能特别重要。
这里有一些代码可以进行实验。
class B
{
unsigned long val;
public:
B(const unsigned long& x) : val(x)
{ std::cout << "storing " << x << std::endl;}
~B() { std::cout << "dropping " << val << std::endl;}
};
typedef std::unique_ptr<B> B_ptr;
class A {
std::vector<B_ptr> vb;
public:
void add(B_ptr&& b)
{ vb.push_back(std::move(b)); } // or even better use emplace_back
};
void f() {
A a;
B_ptr b(new B(123)),c;
a.add(std::move(b));
std::cout << "---" <<std::endl;
a.add(B_ptr(new B(4567))); // unnamed argument does not need std::move
}
写入时,输出为
storing 123
---
storing 4567
dropping 123
dropping 4567
请注意,值是按存储在向量中的顺序销毁的。尝试更改方法add
的原型(必要时调整其他代码以使其编译),以及它是否真的传递了参数b
。可以获得输出行的几种排列。
是的,应该这样做。您正在明确地将所有权从main
转移到A
。这与您以前的代码基本相同,只是它更明确、更可靠。
所以我的简单问题是:这是做这件事的方法吗;移动(b)";唯一的方法是什么?(我在想右价参考,但我不完全理解,所以…)
如果你有一个链接,里面有移动语义的完整解释,unique_ptr。。。我找不到的,不要犹豫。
无耻的插头,搜索标题";移入成员";。它准确地描述了你的场景。
main
中的代码可以简化一点,因为C++14:
a.add( make_unique<B>() );
在这里,您可以将B
的构造函数的参数放在内圆括号内。
您还可以考虑一个拥有原始指针所有权的类成员函数:
void take(B *ptr) { vb.emplace_back(ptr); }
并且CCD_ 40中对应的代码将是:
a.take( new B() );
另一个选项是使用完美转发添加向量成员:
template<typename... Args>
void emplace(Args&&... args)
{
vb.emplace_back( std::make_unique<B>(std::forward<Args>(args)...) );
}
主要代码:
a.emplace();
其中,和以前一样,可以将B
的构造函数参数放在括号内。
链接到工作示例
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 使用指向成员的指针将成员函数作为参数传递
- 如何将参数传递给正在使用模板的类
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 修改函数中的指针(将另一个指针作为参数传递)
- 如何将部分流作为参数传递
- 我正在开发服务器,ip作为参数传递不起作用
- 将成员函数指针作为参数传递给模板方法
- 如何在C++中将迭代器作为函数参数传递
- 将附加参数传递给使用 beast::bind_front_handler 调用的函数
- 如何将一个类的函数作为另一个类的另一个函数的参数传递
- 将参数传递为"const"的奇怪效果
- 如何在 c++ 中将函数作为参数传递?
- 在 C++ 中将非指定类型作为参数传递的最佳方法?
- 使用引用与指针将数组作为参数传递
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 将参数打包的参数传递到 std::queue 中,以便稍后使用不同的函数调用
- 如何使用非类型参数传递模板化类的 Ref 或 Ptr
- 如何调用传递的函数(函数 ptr 作为参数传递给函数)
- 将字符串引用作为参数传递时Ptr错误