C++ C++;11-移动语义在构造上很慢
此代码C++ C++;11-移动语义在构造上很慢,c++,struct,c++11,move-semantics,C++,Struct,C++11,Move Semantics,此代码 #include <iostream> #include <vector> struct obj { std::string name; int age; float money; obj():name("NO_NAME"),age(0),money(0.0f){} obj(const std::string& _name, const int& _age, const float& _money):name(_na
#include <iostream>
#include <vector>
struct obj
{
std::string name;
int age;
float money;
obj():name("NO_NAME"),age(0),money(0.0f){}
obj(const std::string& _name, const int& _age, const float& _money):name(_name),age(_age),money(_money){}
obj(obj&& tmp): name(tmp.name), age(tmp.age), money(tmp.money) {}
obj& operator=(obj&&) {return *this;}
};
int main(int argc, char* argv[])
{
std::vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj("Jon", 45, 500.6f));
}
return(0);
}
或
而不是
v.emplace_back(obj("Jon", 45, 500.6f));
试一试
push_back
具有启用移动的重载<代码>安放_back用于就地施工
编辑:R.Martinho Fernandes所说的。:)
这可能是您想要的:
struct obj
{
std::string name;
int age;
float money;
obj()
: name("NO_NAME")
, age(0)
, money(0.0f)
{
}
obj(std::string _name, int _age, float _money)
: name(std::move(_name))
, age(std::move(_age))
, money(std::move(_money))
{
}
};
int main(int argc, char* argv[])
{
std::vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back("Jon", 45, 500.6f);
}
return(0);
}
结构对象
{
std::字符串名;
智力年龄;
浮动货币;
obj()
:名称(“无名称”)
,年龄(0岁)
,货币(0.0f)
{
}
obj(标准::字符串_名称、整数_年龄、浮动_货币)
:名称(标准::移动(_名称))
,年龄(标准::移动(_年龄))
,钱(标准::移动(_money))
{
}
};
int main(int argc,char*argv[])
{
std::向量v;
对于(int i=0;i<5000000;++i)
{
v、 后置炮台(“乔恩”,45500.6f);
}
返回(0);
}
请注意,我将您的obj(std::string\u name,int\u age,float\u money)
构造函数更改为移动\u name
,而不是制作不必要的副本
您的呼叫也不正确,应该是emplace\u back(“Jon”,45500.6f)
所有其他内容由编译器自动以最佳方式生成。您应该将数据从参数移动到移动构造函数:
obj(obj&& tmp)
:
name(std::move(tmp.name)), age(std::move(tmp.age)), money(std::move(tmp.money)) {}
尽管如果您正确使用后向定位
,这应该是不相关的。不做任何事情更好。你试图让它更快(比什么更快?在你编写移动构造函数之前你真的分析过吗?),但是你打破了它
编译器免费生成复制和移动构造函数和赋值运算符,她做得很好。如果你决定自己写,你就是在告诉编译器你知道得更好,所以她就不碍事了,让你自己改进
你打破的第一件事是,你让你的移动构造函数实际上是复制的。具有名称的对象是左值,即使它们是右值引用,也不能隐式移动左值。因此初始化者需要实际调用std::move
第二个问题是,您没有通过向move构造函数添加noexcept
来声明它没有抛出。编译器生成的一个具有此功能。通过不声明不会引发异常,std::vector
的实现在重新分配底层存储时可能不会使用移动:如果不保证移动不会引发异常,它就无法提供强异常保证
这样做会让它表现得更好吗?大概也许不是。您的实现可能正在对std::string
进行小字符串优化,这意味着没有动态分配:整个字符串“Jon”
,由于较小,将直接存储在std::string
对象中。这就使得移动的成本与复制的成本相同
您可以通过动态分配和使用
unique\u ptr
使整个obj
结构利用便宜的移动。这将使移动比复制更便宜,即使在存在小字符串优化的情况下也是如此。然而,您正在为这种便宜付出分配和额外间接的代价。运行时间主要由string文本中std::string的构造决定,因此move构造和emplace构造之间的差别很小
这在我的机器上需要400毫秒:
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
string name;
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{"Jon", 45, 500.6f});
}
return v.size();
}
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{45, 500.6f});
}
return v.size();
}
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
int t = 0;
for( int i = 0; i < 5000000; ++i )
{
string s("Jon");
t += s.size();
}
return t;
}
#包括
#包括
使用名称空间std;
结构对象
{
字符串名;
智力年龄;
浮动货币;
};
int main(int argc,char*argv[])
{
向量v;
对于(int i=0;i<5000000;++i)
{
v、 后置炮台(目标{“Jon”,45500.6f});
}
返回v.size();
}
这在我的机器上需要80毫秒:
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
string name;
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{"Jon", 45, 500.6f});
}
return v.size();
}
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{45, 500.6f});
}
return v.size();
}
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
int t = 0;
for( int i = 0; i < 5000000; ++i )
{
string s("Jon");
t += s.size();
}
return t;
}
#包括
#包括
使用名称空间std;
结构对象
{
智力年龄;
浮动货币;
};
int main(int argc,char*argv[])
{
向量v;
对于(int i=0;i<5000000;++i)
{
v、 后侵位(obj{45500.6f});
}
返回v.size();
}
请注意,普通结构将为其生成一个合理的默认移动构造函数
这在我的机器上已经需要220毫秒:
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
string name;
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{"Jon", 45, 500.6f});
}
return v.size();
}
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{45, 500.6f});
}
return v.size();
}
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
int t = 0;
for( int i = 0; i < 5000000; ++i )
{
string s("Jon");
t += s.size();
}
return t;
}
#包括
#包括
使用名称空间std;
int main(int argc,char*argv[])
{
int t=0;
对于(int i=0;i<5000000;++i)
{
字符串s(“Jon”);
t+=s.尺寸();
}
返回t;
}
好吧,好吧。。。如果你想搬家。。。你不应该试着搬家吗?move构造函数不移动任何东西;它复制所有内容(提示:不需要调用std::move
anywhere)。仅供参考,编译器生成的move-ctor将做正确的事情。@R.MartinhoFernandes“move-semantic”应该“move”东西,这并不是隐含的事实?只有右值被隐式移动。move构造函数的成员初始化列表中的所有内容都有名称,因此都是左值。我想令人困惑的是,您没有意识到命名的右值引用是左值。@R.MartinhoFernandes mmm,好吧,现在我得到的唯一一件事是,使用这个std::move,我的move构造函数效率非常低,它仍然无法克服推送,如何改进这一点?在链接版本中,我在push_back
上得到了1.61,在emplace_back
上得到了1.39。我已经尝试过了,它仍然比push_back慢,慢得多。@moswald您还需要在move构造函数中添加noexcept,否则gcc std::vector根本不会移动任何东西(在插入或重新分配时)@除非你声明了一个,或者除非你声明了一个复制构造函数。或者,除非您使用MSVC10 .@ MasWald作为完整性,否则,您可能需要考虑将移动构造函数声明为<代码> =默认值;代码>并让编译器生成它。当然,假设默认语义是可取的。@moswald受标准的影响。23.2.1.10“如果push_back()或push_front()函数引发异常,则该函数没有e