25

这段代码:

class X {
  int member;  
};

volatile X a;
X b = a;

失败并出现错误:

prog.cpp:6:7: error: no matching function for call to ‘X::X(volatile X&)’
prog.cpp:6:7: note: candidates are:
prog.cpp:1:7: note: X::X()
prog.cpp:1:7: note:   candidate expects 0 arguments, 1 provided
prog.cpp:1:7: note: X::X(const X&)
prog.cpp:1:7: note:   no known conversion for argument 1 from ‘volatile X’ to ‘const X&’

有什么方法可以让编译器为我生成一个 volatile 复制构造函数?

4

2 回答 2

16

简短的回答是:因为标准说你不会。

C++ 标准 12.8/9(草案 N3242)告诉:

类 X 的隐式声明的复制构造函数将具有以下形式

  • X::X(常量 X&)

如果

  • X 的每个直接或虚拟基类 B 都有一个复制构造函数,其第一个参数的类型为 const B& 或 const volatile B&,并且
  • 对于 X 的所有属于类类型 M(或其数组)的非静态数据成员,每个此类类型都有一个复制构造函数,其第一个参数的类型为 const M& 或 const volatile M&。[注:119]

否则,隐式声明的复制构造函数将具有以下形式

  • X::X(X&)

注释 119 说:

这意味着隐式声明的复制构造函数的引用参数不能绑定到 volatile 左值;见C.1.9

在 C.1.9 中,您会发现:

隐式声明的复制构造函数和隐式声明的复制赋值运算符不能复制 volatile 左值。例如,以下内容在 ISO C 中有效:

struct X { int i; };
volatile struct X x1 = {0};
struct X x2(x1); // invalid C++
struct X x3;
x3 = x1; // also invalid C++

理由:对几个备选方案进行了长时间的辩论。将参数更改为 volatile const X& 将大大复杂化类对象的有效代码的生成。为这些隐式定义的操作提供两个替代签名的讨论引发了关于产生歧义和使根据基础和成员指定这些运算符的形成的规则复杂化的未解决的担忧。

于 2013-06-20T16:33:15.153 回答
-3

关键问题是您没有提供赋值构造函数。因此,编译器会为您生成一个默认值

X& X::operator =(const X& x){
   this.member = x.member;
   return *this;
}

默认赋值构造函数接受参数类型为const X&,其中const低级 const,不会作为顶级 const被忽略。

您的代码X b = a意味着调用默认构造函数。但是您的参数a的类型为volatile X(可以转换为volatile X &volatile const X &)不能隐式转换为const X& 。

因此,您应该将自己的赋值构造函数定义为

X& X::operator =(volatile const X&);

编辑
令我震惊的是,这么多人认为使用赋值运算符时会调用复制构造函数(或调用复制初始化) 。也许称其为赋值运算符并不常见。但是,我关心的是调用哪种方法。

可以参考这篇文章:Copy constructors, assignment operators, and exception safe assignment and Default assignment operator


编辑
我以前犯了一个错误。 X b = a只是一个初始化过程。不涉及分配。为我的错误信息道歉。

于 2013-06-20T16:13:22.533 回答