我可以添加从易失性T到T的隐式转换吗?

时间:2013-06-20 17:54:50

标签: c++ casting operator-overloading

This code

struct T {
    int m_x;
    T(int x) : m_x(x) {}

    operator T() {
        return T(0);
    }
};

int main() {
    volatile T v(2);

    T nv(1);
    nv = v; // nv.m_x = 0
}

给出:

prog.cpp: In function ‘int main()’:
prog.cpp:14:10: error: no match for ‘operator=’ in ‘nv = v’
prog.cpp:14:10: note: candidates are:
prog.cpp:1:8: note: T& T::operator=(const T&)
prog.cpp:1:8: note:   no known conversion for argument 1 from ‘volatile T’ to ‘const T&’
prog.cpp:1:8: note: T& T::operator=(T&&)
prog.cpp:1:8: note:   no known conversion for argument 1 from ‘volatile T’ to ‘T&&’

我需要定义什么类型转换才能使其工作?

3 个答案:

答案 0 :(得分:4)

答案简短:

是的,你可以,但编译器不会为你完成这项工作。

您不能使用编译器提供的从volatile T到T的转换,而是使用volatile限定的构造函数进行用户定义的隐式转换。

<小时/> 通过使用特殊成员函数的显式默认版本来声明这样的转换也是不可能的(参见长答案以供参考)。

您必须提供用户定义的转化方式才能启用此类转让。你可以

  • 使用带有cv限定参数的非显式复制构造函数进行隐式用户定义转换或
  • 复制赋值运算符采用v限定参数。

示例:

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

<小时/>

标准引号&n; n或

的长答案

为什么编译器不为我做这个?

方式1:来自volatile T

的用户提供的构造函数

标准,ISO 14882:2011,4 / 3

  

当且仅当声明 e 格式正确时,表达式 T t=e; 才能隐式转换为T类型发明了临时变量t(8.5)。

由于声明T t = e;,在这种情况下e的类型为volatile T,因此要求复制初始化有效,您需要一个复制构造函数挥发性的。

我已经回答了(Why am I not provided with a default copy constructor from a volatile?)。 因此,您需要提供一种用户定义的方法,即从易失性T中复制初始化T.

X (X const volatile & xo);

注意:

  • 这是一个声明,您还必须提供一个定义。
  • 构造函数不得显式。
  • 提供一个使用volatile参数的用户定义的复制构造函数将导致隐式生成的默认赋值运算符的消除。

这将使您的作业有效。

方式2:用户提供的来自volatile T

的复制赋值运算符

另一种使示例代码分配工作的方法是复制赋值运算符。

不幸的是,标准也表示编译器不会为volatile到非易失性对象的转换提供隐式复制赋值运算符。

标准,ISO 14882:2011,12.8 / 18

  

如果类定义没有显式声明一个复制赋值运算符,则会隐式声明一个。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制赋值运算符被定义为已删除;否则,它被定义为默认值(8.4)。如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用后一种情况。类X的隐式声明的复制赋值运算符将具有

形式      

<强> X&安培; X :: operator =(const X&amp;)

     

如果

     
      
  • X的每个直接基类B都有一个复制赋值运算符,其参数类型为const B&amp; const volatile B&amp;或B,和
  •   
  • 对于类型为M(或其数组)的X的所有非静态数据成员,每个这样的类类型具有复制赋值运算符,其参数是const M&amp;,const volatile M&amp;或M. 122
  •   
     

否则,隐式声明的复制赋值运算符将具有

形式      

<强> X&安培; X ::运算符=(X&安培;)

关于12.8 / 18

的注释122
  

隐式声明的复制赋值运算符的引用参数不能绑定到volatile lvalue;见 C.1.9

other answer我引用的C.1.9中说:

  

隐式声明的复制构造函数和隐式声明的复制赋值运算符无法复制volatile lvalue。 [...]

结果是,如果我们想拥有一个合适的副本赋值运算符,我们必须提供。

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

另请注意,您无法从volatile明确默认声明赋值/构造函数。

C ++ 11标准8.4.2 / 1

  

明确默认的功能

     
      
  • 是一个特殊的会员功能,
  •   
  • 具有相同的声明函数类型(可能不同的ref限定符除外,在复制构造函数或复制赋值运算符的情况下,参数类型可以是“引用非const T”,其中T是成员函数的类的名称)就像它已被隐式声明一样,并且
  •   
  • 没有默认参数。
  •   

以下注释已从最终的C ++ 11标准中删除,但存在于N3242草案中。它仍然存在。

  

[注意:这意味着参数类型,返回类型和 cv-qualifiers必须与假设的隐式声明匹配。 -end note]

由于假设的隐式声明是非易失性的,因此您无法使用默认声明。

答案 1 :(得分:1)

以下是如何获得允许使用volatile源的复制构造函数和复制赋值:

struct X {
  X(const X& o) : members(o.members) {}
  X(const volatile X& o) : members(o.members) {}
  X& operator=(const X& o) {v=o.v; return *this;}
  X& operator=(const volatile X& o) {v=o.v; return *this;}
};

请注意,这会产生一些后果。这种类型不再是POD,甚至不是可复制的。这可能会破坏使其变得不稳定的全部意义。

答案 2 :(得分:0)

您可以实现赋值运算符=

T& operator=(const volatile T &rhs) {
    m_x = rhs.m_x;
    return *this;
}