看看这段代码:
class Foo
{
public:
string name;
Foo(string n) : name{n}
{
cout << "CTOR (" << name << ")" << endl;
}
Foo(Foo&& moved)
{
cout << "MOVE CTOR (moving " << moved.name << " into -> " << name << ")" << endl;
name = moved.name + " ###";
}
~Foo()
{
cout << "DTOR of " << name << endl;
}
};
Foo f()
{
return Foo("Hello");
}
int main()
{
Foo myObject = f();
cout << …Run Code Online (Sandbox Code Playgroud) 我对此有一个后续问题:Move unique_ptr: Reset the source vs. destroy the old object
为了快速总结原始问题,cppreference上有以下示例代码:
struct List
{
struct Node
{
int data;
std::unique_ptr<Node> next;
};
std::unique_ptr<Node> head;
~List()
{
// destroy list nodes sequentially in a loop, the default destructor
// would have invoked its `next`'s destructor recursively, which would
// cause stack overflow for sufficiently large lists.
while (head)
head = std::move(head->next);
}
...
};
Run Code Online (Sandbox Code Playgroud)
答案告诉我,析构函数中的循环就所包含的原始指针而言是明确定义的,因为unique_ptr::operator=(unique_ptr&& other)被定义为 call ,这保证了在删除所持有的 raw_pointer 之前reset(other.release())提取原始指针。otherthis
我相信 cppreference …
我最近一直在研究右值参考,并得出结论,在任何地方使用pass-by-value进行完整的对象复制是非常有利的(为了完整的理由,请参阅例如如何在添加rvalue参考运算符时减少冗余代码重载?而要速度?按值传递!),因为编译器可以自动在如情况下优化复制走f(std::move(a));,其中f被定义为void f(A a);.
传递价值的一个负面后果是,std::move即使在简单的情况下,所有代码都会被弄乱,例如:
void Object::value(A a)
{
value_ = std::move(a);
}
Run Code Online (Sandbox Code Playgroud)
显然,如果我只写下面的内容:
void Object::value(A a)
{
value_ = a;
}
Run Code Online (Sandbox Code Playgroud)
a即使没有提示,编译器也应该很难识别出它的生命周期即将结束,并且不会使用额外的副本来惩罚我.事实上,即使在复杂的函数中,编译器也应该能够识别它.
问题:
C++ 0x标准是否允许此优化?
编译器是否使用它?即使在复杂的情况下,即函数由多行组成?
这种优化的可靠性如何,即我希望编译器能够像我期望编译器应用返回值优化那样使用它吗?
可以将push_back不可复制但可移动类型的rvalues转换为该类型的向量:
#include <vector>
struct S
{
S(int);
S(S&&);
};
int main()
{
std::vector<S> v;
v.push_back(S(1));
v.push_back(S(2));
v.push_back(S(3));
}
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试初始化列表构造具有相同rvalues的向量时,我得到关于所需的复制构造函数的错误:
#include <vector>
struct S
{
S(int);
S(S&&);
};
int main()
{
std::vector<S> v = {S(1), S(2), S(3)};
}
Run Code Online (Sandbox Code Playgroud)
我在GCC 4.7中遇到以下错误:
In file included from include/c++/4.7.0/vector:63:0,
from test.cpp:1:
include/c++/4.7.0/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = S, _Args = {const S&}]':
include/c++/4.7.0/bits/stl_uninitialized.h:77:3: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const S*, _ForwardIterator = S*, …Run Code Online (Sandbox Code Playgroud) 我知道回归通常不是一个好主意std::move,即
bigObject foo() { bigObject result; /*...*/ return std::move(result); }
Run Code Online (Sandbox Code Playgroud)
而不是简单的
bigObject foo() { bigObject result; /*...*/ return result; }
Run Code Online (Sandbox Code Playgroud)
因为它妨碍了返回值优化.但是在具有多个不同回报的函数的情况下,特别是类似的东西
class bar {
bigObject fixed_ret;
bool use_fixed_ret;
void prepare_object(bigObject&);
public:
bigObject foo() {
if(use_fixed_ret)
return fixed_ret;
else{
bigObject result;
prepare_object(result);
return result;
}
}
};
Run Code Online (Sandbox Code Playgroud)
我认为在这样的函数中,正常的返回值优化是不可能的,所以投入是一个好主意
return std::move(result);
Run Code Online (Sandbox Code Playgroud)
在这里,或者我应该做什么(IMO丑陋,但这是有争议的)
bigObject foo() {
bigObject result;
if(use_fixed_ret)
result = fixed_ret;
else{
prepare_object(result);
}
return result;
}
Run Code Online (Sandbox Code Playgroud) 我已经写了很长一段时间的C++ 11代码了,并没有对它进行任何基准测试,只是期望像移动语义这样的矢量操作现在"只是更快".因此,当实际使用GCC 4.7.2和clang 3.0(Ubuntu 12.10 64位上的默认编译器)进行基准测试时,我得到了非常不满意的结果.这是我的测试代码:
编辑:至于张贴@DeadMG和@ronag的(好)的答案,我从改变的元素类型std::string,以my::string不具有swap(),并且所有的内部串大(200-700字节),因此他们不应该SSO 的受害者.
编辑2: COW是原因.由伟大的意见再次改编代码,更改从存储std::string到std::vector<char>和离开了复制/移动onstructors(让编译器生成它们来代替).没有COW,速度差异实际上是巨大的.
编辑3:编译时重新添加以前的解决方案-DCOW.这使得内部存储成为std::string而不是std::vector<char>@chico所要求的.
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <functional>
static std::size_t dec = 0;
namespace my { class string
{
public:
string( ) { }
#ifdef COW
string( const std::string& ref ) : str( ref ), val( dec % 2 ? - ++dec : ++dec ) …Run Code Online (Sandbox Code Playgroud) 在他的2013年GoingNative演讲中,Scott Meyers指出,std::move并不能保证生成的代码实际上会执行一个动作.
例:
void foo(std::string x, const std::string y) {
std::string x2 = std::move(x); // OK, will be moved
std::string y2 = std::move(y); // compiles, but will be copied
}
Run Code Online (Sandbox Code Playgroud)
这里,不能应用移动构造函数,但由于重载分辨率,将使用普通的复制构造函数.这个后备选项对于向后兼容C++ 98代码可能是至关重要的,但在上面的例子中,它很可能不是程序员想要的.
有没有办法强制调用移动构造函数?
例如,假设您要移动一个巨大的矩阵.如果您的应用程序确实依赖于要移动的Matrix,那么如果无法移动则立即获得编译错误会很棒.(否则,您可能会通过单元测试轻松地解决性能问题,并且只能在进行一些分析后才能发现.)
让我们称之为保证行动strict_move.我希望能够编写这样的代码:
void bar(Matrix x, const Matrix y) {
Matrix x2 = strict_move(x); // OK
Matrix y2 = strict_move(y); // compile error
}
Run Code Online (Sandbox Code Playgroud)
可能吗?
编辑:
谢谢你的答案!有一些合理的要求澄清我的问题:
strict_move输入是const,应该失败吗?strict_move结果不会导致实际的移动操作(即使副本可能像移动一样快),也应该失败,例如,const complex<double>?我最初的想法非常含糊:我认为Scott Meyers的例子非常令人担忧,所以我想知道是否有可能让编译器阻止这种非预期的副本.
Scott Meyers在他的演讲中提到,一般的编译器警告不是一种选择,因为它会导致大量误报.相反,我想与编译器进行通信,例如"我100%确定这必须始终导致移动操作,并且副本对于此特定类型来说太昂贵".
因此,我会毫不客气地说,strict_move两种情况都应该失败.与此同时,我不确定什么是最好的.我没有考虑的另一个方面是 …
在上一个问题之后,我一直在研究ref-qualifiers.
给出下面的代码示例;
#include <iostream>
#include <string>
#include <utility>
struct A {
std::string abc = "abc";
std::string& get() & {
std::cout << "get() &" << std::endl;
return abc;
}
std::string get() && {
std::cout << "get() &&" << std::endl;
return std::move(abc);
}
std::string const& get() const & {
std::cout << "get() const &" << std::endl;
return abc;
}
std::string get() const && {
std::cout << "get() const &&" << std::endl;
return abc;
}
};
int main()
{
A a1;
a1.get();
const …Run Code Online (Sandbox Code Playgroud) 我最近意识到在C++ 11中添加移动语义(或者至少我的实现,Visual C++)已经积极地(并且非常显着地)打破了我的一个优化.
请考虑以下代码:
#include <vector>
int main()
{
typedef std::vector<std::vector<int> > LookupTable;
LookupTable values(100); // make a new table
values[0].push_back(1); // populate some entries
// Now clear the table but keep its buffers allocated for later use
values = LookupTable(values.size());
return values[0].capacity();
}
Run Code Online (Sandbox Code Playgroud)
我遵循这种模式来执行容器回收:我将重新使用相同的容器而不是销毁和重新创建它,以避免不必要的堆释放和(立即)重新分配.
在C++ 03上,这很好用 - 这意味着这段代码用于返回1,因为向量是按元素复制的,而它们的底层缓冲区保持原样.因此,我可以修改每个内部向量,因为它知道它可以使用与之前相同的缓冲区.
然而,在C++ 11上,我注意到这会导致右侧移动到左侧,这对左侧的每个向量执行元素移动分配.这反过来导致向量丢弃其旧缓冲区,突然将其容量减少到零.因此,由于堆分配/释放过多,我的应用程序现在会大大减慢速度.
我的问题是:这种行为是一个错误,还是故意的?是否甚至由标准指定?
我刚刚意识到这种特定行为的正确性可能取决于是否a = A()可以使指向元素的迭代器无效a.但是,我不知道移动赋值的迭代器失效规则是什么,所以如果你知道它们,你的答案中可能值得一提.
一个unique_ptr不能被推回到一个std::vector,因为它是不可复制的,除非std::move被使用。但是,让我们F使用一个返回a的函数unique_ptr,然后std::vector::push_back(F())允许该操作。下面是一个示例:
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int f() { return _f + 10; }
private:
int _f = 20;
};
std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }
int main() {
std::unique_ptr<A> p1(new A());
std::vector< std::unique_ptr<A> > v;
v.push_back(p1); // (1) This fails, should use std::move
v.push_back(create()); // (2) This doesn't fail, should use std::move?
return 0;
}
Run Code Online (Sandbox Code Playgroud)
(2)允许,但(1)不允许。这是因为返回的值被隐式地移动了吗?
在中(2) …
c++ ×10
move-semantics ×10
c++11 ×8
clang ×1
gcc ×1
move ×1
optimization ×1
performance ×1
stdvector ×1
unique-ptr ×1
vector ×1