是std :: move真的需要初始化列表的构造函数重量成员传递的值? [英] Is std::move really needed on initialization list of constructor for heavy members passed by value?

查看:145
本文介绍了是std :: move真的需要初始化列表的构造函数重量成员传递的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近阅读了一个来自 cppreference ... / vector / emplace_back 的示例:

  struct President 
{
std :: string name;
std :: string country;
int year;

总统(std :: string p_name,std :: string p_country,int p_year)
:name(std :: move(p_name)),country(std :: move(p_country) ),year(p_year)
{
std :: cout< 我正在建造。
}


$ b $ p我的问题是: std :: move 真的需要吗?我的观点是,这个 p_name 不是在构造函数的主体中使用的,所以,也许,在语言中有一些规则默认使用移动语义。 p>

这将真的很烦人的添加std :: move在初始化列表中的每个重成员(如 std :: string std :: vector )。想象一下,在C ++ 03中编写的数百个KLOC项目 - 我们应该在这个 std :: move 吗?



<此问题: move-constructor-and-initialization-list 答案说:


作为一个黄金规则,每当你通过rvalue引用获取一些东西时,你的
需要在std :: move中使用它,通过
通用引用(即推导出模板类型与&&),你需要
在std :: forward中使用它


但我不知道:传值通过而不是通用引用?



[UPDATE]





在这个例子中,AFAIK我们不使用 std :: move

  std :: string getName()
{
std :: string local =你好SO!
return local; // std :: move(local)不需要也可能不正确
}

,这里需要它:

  void President :: setDefaultName()
{
std :: string local =SO;
name = local; // std :: move OR not std :: move?
}

对于我来说,这个局部变量是过期的变量,因此可以应用移动语义。 ..这类似于通过值传递的参数....

解决方案


这个std :: move真的需要吗?我的观点是
编译器看到这个p_name不是在构造函数体中使用的,
so,也许,有一些规则默认使用move语义。


一般来说,当你想把一个左值转换为右值时,是,你需要一个 std :: move ) 。另请参见 Do C ++ 11编译器在代码优化期间可以将本地变量转换为右值?

  void President :: setDefaultName()
{
std :: string local =SO;
name = local // std :: move OR not std :: move?
}




对于我这个局部变量是expiring变量 - 所以移动语义
可以应用...这类似于通过值传递的参数....


在这里,我希望优化器消除多余的 local ALTOGETHER;不幸的是,实际上并不是这样。编译器优化在 内存发生时变得棘手,请参阅 BoostCon 2013主题演讲: Chandler Carruth:优化C ++的紧急结构。我从Chandler的谈话中得到的一个结论是,优化器在堆分配的内存方面只是倾向于放弃。



查看下面的代码是一个令人失望的例子。在这个例子中,我不使用 std :: string ,因为这是一个具有内联汇编代码的大量优化的类,通常产生违反直觉的生成代码。为了增加伤害伤害, std :: string 大致上是一个引用计数共享指针在gcc 4.7.2至少(copy-on-write optimization ,现在被 std :: string 的2011标准禁止。所以示例代码不含 std :: string

  #include< ;算法> 
#include< cstdio>

int main(){
char literal [] = {string literal};
int len = sizeof literal;
char * buffer = new char [len];
std :: copy(literal,literal + len,buffer);
std :: printf(%s\\\
,buffer);
delete [] buffer;
}

很明显,根据as-if规则, em>可以优化为:

  int main(){
std :: printf string literal\\\
);
}

我试过GCC 4.9.0和Clang 3.5的链接时间优化(LTO),并且它们都不能将代码优化到此级别。我看了生成的汇编代码:他们都分配了堆上的内存并做了副本。

$ b $

  #include< algorithm> 
#include< cstdio>

int main(){
char literal [] = {string literal};
const int len = sizeof literal;
char buffer [len];
std :: copy(literal,literal + len,buffer);
std :: printf(%s\\\
,buffer);
}



我检查了汇编代码:这里,编译器能够减少代码基本上只是 std :: printf(string literal\\\
);



所以我希望你的示例代码中多余的 local 可以被消除不是完全不支持的:示例使用堆栈分配的数组显示,可以做。


想象几百个用C ++ 03编写的KLOC项目 - 到处这个 std :: move

[...]

但是我不知道:传值不通用参考?


想要速度?测量(通过 Howard Hinnant



你可以很容易地发现自己在一个情况下,你做你的优化,只是为了发现你的优化使代码更慢。 (我的建议和Howard Hinnant的:Measure一样。

  std :: string getName()
{
std :: string local =Hello SO!;
return local; // std :: move(local)不需要也可能不正确
}

是的,但我们有这种特殊情况的规则:它被称为命名返回值优化(NRVO)。


Recently I read an example from cppreference.../vector/emplace_back:

struct President
{
    std::string name;
    std::string country;
    int year;

    President(std::string p_name, std::string p_country, int p_year)
        : name(std::move(p_name)), country(std::move(p_country)), year(p_year)
    {
        std::cout << "I am being constructed.\n";
    }

My question: is this std::move really needed? My point is that this p_name is not used in the body of constructor, so, maybe, there is some rule in the language to use move semantics for it by default?

That would be really annoying to add std::move on initialization list to every heavy member (like std::string, std::vector). Imagine hundreds of KLOC project written in C++03 - shall we add everywhere this std::move?

This question: move-constructor-and-initialization-list answer says:

As a golden rule, whenever you take something by rvalue reference, you need to use it inside std::move, and whenever you take something by universal reference (i.e. deduced templated type with &&), you need to use it inside std::forward

But I am not sure: passing by value is rather not universal reference?

[UPDATE]

To make my question more clear. Can the constructor arguments be treated as XValue - I mean expiring values?

In this example AFAIK we do not use std::move:

std::string getName()
{
   std::string local = "Hello SO!";
   return local; // std::move(local) is not needed nor probably correct
}

So, would it be needed here:

void President::setDefaultName()
{
   std::string local = "SO";
   name = local; // std::move OR not std::move?
}

For me this local variable is expiring variable - so move semantics could be applied... And this similar to arguments passed by value....

解决方案

My question: is this std::move really needed? My point is that compiler sees that this p_name is not used in the body of constructor, so, maybe, there is some rule to use move semantics for it by default?

In general, when you want to turn an lvalue to an rvalue, then yes, you need a std::move(). See also Do C++11 compilers turn local variables into rvalues when they can during code optimization?

void President::setDefaultName()
{
   std::string local = "SO";
   name = local; // std::move OR not std::move?
}

For me this local variable is expiring variable - so move semantics could be applied... And this similar to arguments passed by value....

Here, I would want the optimizer to eliminate the superfluous local ALTOGETHER; unfortunately, it is not the case in practice. Compiler optimizations get tricky when heap memory comes in to play, see BoostCon 2013 Keynote: Chandler Carruth: Optimizing the Emergent Structures of C++. One of my takeaways from Chandler's talk is that optimizers simply tend to give up when it comes to heap allocated memory.

See the code below for a disappointing example. I don't use std::string in this example because that's a heavily optimized class with inline assembly code, often yielding counterintuitive generated code. To add injury to insult, std::string is roughly speaking a reference counted shared pointer in gcc 4.7.2 at least (copy-on-write optimization, now forbidden by the 2011 standard for std::string). So the example code without std::string:

#include <algorithm>
#include <cstdio>

int main() {
   char literal[] = { "string literal" };
   int len = sizeof literal;
   char* buffer = new char[len];
   std::copy(literal, literal+len, buffer);
   std::printf("%s\n", buffer);
   delete[] buffer;
}

Clearly, according to the "as-if" rule, the generated code could be optimized to this:

int main() {
   std::printf("string literal\n");
}

I have tried it with GCC 4.9.0 and Clang 3.5 with link time optimizations enabled (LTO), and none of them could optimize the code to this level. I looked at the generated assembly code: They both allocated the memory on the heap and did the copy. Well, yeah, that's disappointing.

Stack allocated memory is different though:

#include <algorithm>
#include <cstdio>

int main() {
   char literal[] = { "string literal" };
   const int len = sizeof literal;
   char buffer[len];
   std::copy(literal, literal+len, buffer);
   std::printf("%s\n", buffer);
}

I have checked the assembly code: Here, the compiler is able to reduce the code to basically just std::printf("string literal\n");.

So my expectations that the superfluous local in your example code could be eliminated is not completely unsupported: As my latter example with the stack allocated array shows, it can be done.

Imagine hundreds of KLOC project written in C++03 - shall we add everywhere this std::move?
[...]
But I am not sure: passing by value is rather not universal reference?

"Want speed? Measure." (by Howard Hinnant)

You can easily find yourself in a situation that you do your optimizations just to find out that your optimizations made the code slower. :( My advice is the same as Howard Hinnant's: Measure.

std::string getName()
{
   std::string local = "Hello SO!";
   return local; // std::move(local) is not needed nor probably correct
}

Yes, but we have rules for this special case: It is called named return value optimization (NRVO).

这篇关于是std :: move真的需要初始化列表的构造函数重量成员传递的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆