[英]non-copyable objects and value initialization: g++ vs msvc
我看到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)测试了它,因为复制构造函数是私有的。
两个问题:
T t;
这不是一个可接受的解决方案,因为这打破了POD类型)。 PS我看到与boost :: noncopyable相同的问题。
你在MSVC中看到的行为是一个扩展,虽然它在下一页以迂回的方式记录(强调我的) http://msdn.microsoft.com/en-us/library/0yw5843c.aspx :
等号初始化语法与函数式语法不同,即使生成的代码在大多数情况下是相同的。 不同之处在于,当使用等号语法时,编译器必须表现得好像发生了以下事件序列:
- 创建与正在初始化的对象类型相同的临时对象。
- 将临时对象复制到对象。
在编译器执行这些步骤之前,必须可以访问构造函数。 即使编译器在大多数情况下可以消除临时创建和复制步骤,但是无法访问的复制构造函数会导致等号初始化失败( 在/ Za,/ Ze(禁用语言扩展)下 )。
请参阅Ben Voigt的解决方案,该解决方案是boost::value_initialized
的简化版本,正如Ben的回答中的litb所指出的那样。 boost::value_initalized
的文档对问题,解决方法以及各种编译器问题的一些缺陷进行了很好的讨论。
我不认为需要模板元编程。 尝试
template <class T>
void doit()
{
struct initer { T t; initer() : t() {} } inited;
T& t = inited.t;
...
}
有§12.8/ 14:
如果隐式使用对象的复制构造函数或复制赋值运算符并且无法访问特殊成员函数,则程序格式不正确。
然后是§12.8/ 15:
当满足某些条件时,允许实现省略类对象的复制结构,即使该对象的复制构造函数和/或析构函数具有副作用。
所以,问题是,如果实现省略了对复制构造函数的调用(显然允许这样做),实际使用的是复制构造函数吗?
而且,根据§3.2/ 2,答案是肯定的:
即使调用实际上被实现省略,也会使用复制构造函数。
您是否看到使用/ 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()
,则MSVC会生成另一个警告(C4930) ,尽管GCC很乐意接受它,但没有发出任何警告。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.