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