编译器何时应该生成移动构造函数?

15

我使用VS11并使用以下内容:

class ContextWrapper
{
public:

    ContextWrapper()
    {
    } //it should be defaulted I *guess* in order to have automatic move constructor ?
      // no support in VS11 for that now  

    Context* GetContext()
    {
        return this->context.get();
    }

    void SetContext(std::unique_ptr<Context> context)
    {
        this->context = std::move(context);
    }

    //ContextWrapper(ContextWrapper&& other):  context(std::move(other.context))
    //{
    //} // I would like this to be generated by the compiler

private:
    ContextWrapper(const ContextWrapper&);
    ContextWrapper& operator= (const ContextWrapper&);

    std::unique_ptr<Context> context;
};

我希望这个类有生成的移动构造函数/赋值运算符。我没有一个平凡的构造函数是不是导致我没有得到移动?还是有其他因素影响了这个问题?


2
移动构造函数未生成,因为您声明了一个复制构造函数。请删除私有复制构造函数和复制赋值运算符。 - nosid
5
如果添加一个不可复制的成员(比如unique_ptr),已经可以防止生成复制特殊成员函数,因此没有必要手动防止它们。 - R. Martinho Fernandes
问题在于,如果我不将复制/赋值声明为私有的话,我会得到关于私有unique_ptr复制构造函数的错误。如果我不声明自己的移动构造函数,编译器尝试自动生成复制并失败。 - Ghita
@Ghita 如果您已经有一个不可复制的成员(如std::unique_ptr或引用),编译器就不应该自动生成复制构造函数(和赋值)。实际上,VC也没有这样做。因此,编译器抱怨的唯一原因是如果您在周围代码中尝试复制ContextWrapper。但即使在这种情况下添加私有复制构造函数也无济于事,因为您的类在周围代码中的使用已经出现问题。 - Christian Rau
2
如果你= delete了复制成员函数,那么你将会得到移动成员函数。然而,VC++似乎还没有实现生成移动成员函数的功能。 - Xeo
显示剩余4条评论
2个回答

23
这部分关于C++11的内容还没有确定。无论标准将会说些什么,VC11都不可能实现它。因此,今天我认为你不能指望生成移动成员。
然而,这是一个好问题,我想给出一个好的答案。
通常情况下,如果您没有用户声明的复制成员或析构函数,则编译器应该生成移动成员。= default和= delete算作用户声明。如果您声明了一个移动成员(例如移动构造函数),另一个移动成员将不会隐式生成。
不幸的是,C++11接着说有时候用=default声明的移动成员会被隐式删除,有时候它们的生成取决于基类和成员是否具有移动成员或者是否可以平凡地复制。这一切都太过复杂,有时候会产生出乎意料的行为。这是跟踪此错误的CWG问题:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1402

我撰写此文时,该问题尚未得到正确的建议解决方案。我预计这种情况将在一周左右发生变化。在2012年秋季C++标准会议上,达成了以下协议:
1.编译器永远不会隐式删除移动成员。 2.移动成员的隐式生成始终与“= default”相同。 3.隐式生成不取决于基类或成员的平凡性,也不取决于它们在移动时是否会抛出异常。
简而言之,我期望CWG 1402的更正措辞只是简单地说:
通常,如果您没有用户声明的复制成员或析构函数,则编译器应生成移动成员。“= default”和“= delete”被视为用户声明。如果您声明了一个移动成员(例如移动构造函数),则另一个成员将不会隐式生成。如果您“=default”一个移动成员,您将获得移动每个基类和成员的内容。
(按照适当的标准)。我还没有看到将说明这一点的措辞。Jason Merrill正在为我们撰写这篇文章。
这意味着编译器有时会隐式生成抛出移动成员。但我们追求的是简单规则,大多数情况下都能正确处理(少有惊喜)。

好吧,可以依靠且完全可预测的基础概念已经随着文本模式流(导致在标准C++中无法在Windows中编写cat(不让我们进入echo)的超级愚蠢的情况)而消失了。因此,我认为关于移动语义正在进行的这种澄清并不是很重要。它涉及到优化的复杂细节,这种优化可能会抛出移动构造函数,并且在引入额外故障模式方面已经不可靠 - 因此细节并不重要。 - Cheers and hth. - Alf
1
@Cheersandhth.-Alf,所有的细节都可能非常重要,因为您依赖它来提高性能。幸运的是,据我所知,我们可以编写一个编译时断言来确认无异常移动操作是否可用。 - Ghita

4
为了更直接地回答这个问题,Visual Studio不支持任何版本的隐式移动构造函数/赋值生成。因此,您必须手动编写它们。

我还以为他们在2012年修复了这个问题。 - Christian Rau
@ChristianRau:不过,考虑到Howard说的内容,他们等待可能是个明智的选择。 - Nicol Bolas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接