代码之家  ›  专栏  ›  技术社区  ›  R Samuel Klatchko

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

  •  4
  • R Samuel Klatchko  · 技术社区  · 15 年前

    我看到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; 是不可接受的解决方案,因为这打破了豆荚类型)。

    另外,我发现boost::noncopyable也有同样的问题。

    4 回复  |  直到 15 年前
        1
  •  8
  •   Community CDub    8 年前

    您在msvc中看到的行为是一个扩展,尽管它在下一页中以迂回的方式被记录了下来(重点是我的) http://msdn.microsoft.com/en-us/library/0yw5843c.aspx :

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

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

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

    See Ben Voigt's answer 对于简化版的 boost::value_initialized 利特在对本的回答的评论中指出。文档 boost::value_initalized 对问题、解决方法和各种编译器问题的一些陷阱进行了深入的讨论。

        2
  •  5
  •   Ben Voigt    15 年前

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

    template <class T>
    void doit()
    {
        struct initer { T t; initer() : t() {} } inited;
        T& t = inited.t;
        ...
    }
    
        3
  •  3
  •   James McNellis    15 年前

    第12.8/14节:

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

    然后是第12.8/15节:

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

    所以,问题是,如果实现省略了对copy构造函数的调用(这显然是允许的),那么copy构造函数实际上是 习惯于 ?

    根据第3.2/2条,答案是肯定的:

    即使调用实际上被实现忽略,也会使用复制构造函数。

        4
  •  0
  •   Dustin    15 年前

    你看到用msvc/wall编译时会发生什么吗?它说明了你的班级:

    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
    

    海湾合作委员会补救措施: 为创建副本构造函数 noncopyable (理想情况下是赋值运算符!)尽其所能复制信息 noncopyable_base ,即调用构造函数 不可复制的基 没有参数(因为这是 不可复制的 )然后从 不可复制的基 . 给出了 不可复制的基 但是,似乎没有要复制的数据,所以 noncopyable_base() 到新的初始值设定项列表 noncopyable(const noncopyable &) 功能应该起作用。

    注意msvc对你的程序所说的话。还要注意,如果您使用 T t() 而不是 T t = T() , another warning (C4930) 是由msvc生成的,尽管gcc很高兴接受这两种方式,而没有发出任何警告。

    推荐文章