编译器会在setter方法中自动应用移动语义吗?

时间:2013-03-21 18:39:13

标签: c++ c++11 compiler-optimization move-semantics copy-elision

我想知道是否允许编译器在以下setter方法中自动使用wstring的move构造函数(没有显式调用std :: move):

void SetString(std::wstring str)
{
    m_str = str;  // Will str be moved into m_str automatically or is std::move(str) needed?
}

从我读过的内容听起来似乎不允许编译器做出这个决定,因为str是一个左值,但似乎很明显在这里使用move不会改变程序行为。

禁止移动,是否会应用其他类型的复制省略?

3 个答案:

答案 0 :(得分:3)

  

[是]允许编译器自动使用移动构造函数

是的,这会很好。但这不仅仅是一种优化,这对语言有实际影响。

考虑仅限移动类型unique_ptr

std::unique_ptr<int> f()
{
  std::unique_ptr<int> up;
  return up; // this is ok although unique_ptr is non-copyable.
}

让我们假设您的规则将被包含在C ++标准中,称为“参数的最后一次出现”规则。

void SetString(std::unique_ptr<int> data)
{
    m_data = data; // this must be ok because this is "argument's last occurence"
}

检查返回中是否使用了标识符很容易。检查它是否是“参数的最后一次出现”不是。

void SetString(std::unique_ptr<int> data)
{
    if (condition) {
      m_data = data; // this is argument's last occurence
    } else {
      data.foo();
      m_data = data; // this is argument's last occurence too
    }
    // many lines of code without access to data
}

这也是有效的代码。所以每个编译器都需要检查“参数的最后一次出现”,这不是一件容易的事。为此,他必须扫描整个函数,以确定第一行是否有效。如果你必须向下滚动2页来检查这一点,那么作为人类也难以推理。

不,编译器不允许在C ++ 11中使用。并且他可能不会被允许在未来的标准中,因为这个功能在编译器中很难实现,并且只是为用户带来方便。

答案 1 :(得分:1)

不,这里不会使用移动语义,因为str可以在下一个代码中使用,实际上即使它是rvalue你仍然需要std :: move强制它..如果你想使用移动语义我会建议获得wstring&amp;&amp; str到函数然后使用move ..

答案 2 :(得分:1)

不,不允许使用编译器。由于某些原因,不仅因为很难做到。我认为复制和移动可能会产生副作用,你需要知道什么时候可以使用它们。例如,众所周知,返回一个本地对象会移动它 - 你希望它记录在案,是可以的。

因此,我们有以下可能性:

你的例子:

void SetString(std::wstring str)
{
     m_str = str;  
}

对于r值:str中的一个r-ref,加上m_str的副本。对于l值:str中的副本和m_str中的副本。

我们可以手动“更好”地做到这一点:

void SetString( std::wstring str)
{
     m_str = std::move(str);  
}

对于r值:str中的一个r-ref,再加上m_str。对于l值:str中的副本和m_str中的移动。

如果出于某种原因(你希望它在没有C ++ 11的情况下进行编译而不进行更改,但在移植代码时自动利用C ++ 11?)您不希望“手动优化”您可以执行的代码:

void SetString(const std::wstring& str)
{
     m_str = str;  
}

对于r值:str中的一个参考,以及m_str的副本。对于l值:str中的引用和m_str中的副本。永远不要2份。