不可复制的对象和值初始化:g ++ vs msvc

时间:2010-04-19 23:11:23

标签: c++ gcc visual-c++ value-initialization

我看到g ++和msvc在初始化不可复制对象的值周围有一些不同的行为。考虑一个不可复制的类:

class noncopyable_base
{
public:
    noncopyable_base() {}

private:
    noncopyable_base(const noncopyable_base &);
    noncopyable_base &operator=(const noncopyable_base &);
};

class noncopyable : private noncopyable_base
{
public:
    noncopyable() : x_(0) {}
    noncopyable(int x) : x_(x) {}

private:
    int x_;
};

和一个使用值初始化的模板,以便即使类型为POD,该值也将获得已知值:

template <class T>
void doit()
{
    T t = T();
    ...
}

并试图一起使用它们:

doit<noncopyable>();

从VC ++ 9.0开始,这在msvc上运行正常,但在每个版本的g ++上都失败了我用它(包括版本4.5.0)进行了测试,因为复制构造函数是私有的。

两个问题:

  1. 哪种行为符合标准?
  2. 有关如何在gcc中解决此问题的任何建议(并且要明确,将其更改为T t;不是可接受的解决方案,因为这会破坏POD类型。)
  3. P.S。我看到了与boost :: noncopyable相同的问题。

4 个答案:

答案 0 :(得分:8)

您在MSVC中看到的行为是一个扩展,虽然它在下一页以迂回的方式记录(强调我的)http://msdn.microsoft.com/en-us/library/0yw5843c.aspx

  

等号初始化语法与函数式语法不同,即使生成的代码在大多数情况下是相同的。不同之处在于,当使用等号语法时,编译器必须表现得好像发生了以下事件序列:

     
      
  • 创建与正在初始化的对象相同类型的临时对象。
  •   
  • 将临时对象复制到对象。
  •   
     

在编译器执行这些步骤之前,必须可以访问构造函数。即使编译器在大多数情况下可以消除临时创建和复制步骤,但是无法访问的复制构造函数会导致等号初始化失败( / Za,/ Ze(禁用语言扩展))。

See Ben Voigt's answer有一个变通方法,它是boost::value_initialized的简化版本,正如Ben对答案的评论中所指出的那样。 boost::value_initalized的文档对问题,解决方法以及各种编译器问题的一些缺陷进行了很好的讨论。

答案 1 :(得分:5)

我认为不需要模板元编程。尝试

template <class T>
void doit()
{
    struct initer { T t; initer() : t() {} } inited;
    T& t = inited.t;
    ...
}

答案 2 :(得分:3)

§12.8/ 14:

  

如果隐式使用对象的复制构造函数或复制赋值运算符且无法访问特殊成员函数,则程序格式不正确。

然后是§12.8/ 15:

  

当满足某些条件时,允许实现省略类对象的复制结构,即使该对象的复制构造函数和/或析构函数具有副作用。

所以,问题是,如果实现省略了对复制构造函数的调用(显然允许这样做),复制构造函数实际上是使用吗?

而且,答案是肯定的,根据§3.2/ 2:

  

即使调用实际上已被实现删除,也会使用复制构造函数。

答案 3 :(得分:0)

您是否看到使用/ Wall与MSVC进行编译时会发生什么?它说明了关于你班级的以下内容:

nocopy.cc(21) : warning C4625: 'noncopyable' : copy constructor could not be
generated because a base class copy constructor is inaccessible
nocopy.cc(21) : warning C4626: 'noncopyable' : assignment operator could not be
generated because a base class assignment operator is inaccessible

GCC补救措施: 为noncopyable创建一个复制构造函数(理想情况下是一个赋值运算符!),它可以复制来自noncopyable_base的信息,即调用没有参数的noncopyable_base的构造函数(因为这是noncopyable)可以访问的唯一一个,然后从noncopyable_base复制任何数据。但是,鉴于noncopyable_base的定义,似乎没有要复制的数据,因此将noncopyable_base()简单地添加到新noncopyable(const noncopyable &)函数的初始化列表中应该可以正常工作。

请注意MSVC对您的计划所说的内容。另请注意,如果您使用T t()而不是T t = T()another warning (C4930)由MSVC生成,尽管GCC乐意接受它,但未发出任何警告。