我可以添加从volatile T到T的隐式转换吗?

Eri*_*ric 3 c++ casting operator-overloading

这段代码

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
}
Run Code Online (Sandbox Code Playgroud)

给出:

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&&’
Run Code Online (Sandbox Code Playgroud)

我需要定义什么类型转换重载才能正常工作?

Pix*_*ist 5

简短的答案:

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

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


通过使用特殊成员函数的显式默认版本来声明这种转换也是不可能的(请参见长答案)。

您必须提供一种用户定义的转换方式才能启用此类分配。你可以

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

例:

X (X const volatile & xo);
X& operator= (X const volatile & xo);
Run Code Online (Sandbox Code Playgroud)


用标准引号'n或

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

方法1:易失性T的用户提供的构造函数

标准,ISO 14882:2011、4 / 3

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

由于声明T t = e;(在这种情况下e为type volatile T)需要有效的复制初始化,因此您将需要来自volatile T的复制构造函数。

我已经回答了(为什么我没有从volatile提供默认的副本构造函数?)。因此,您需要提供一种用户定义的从volatile T复制T的方法。

X (X const volatile & xo);
Run Code Online (Sandbox Code Playgroud)

注意:

  • 这是一个声明,您还必须提供一个定义。
  • 构造函数不能是显式的。
  • 提供带有volatile参数的用户定义的副本构造函数将导致隐式生成的默认赋值运算符的缺失。

这将使您的作业工作。

方法2:用户从volatile T提供的副本分配运算符

使您的示例代码进行分配的另一种方法是复制分配运算符。

不幸的是,该标准还指出,编译器不会提供用于将易失性对象转换为非易失性对象的隐式副本分配运算符。

标准,ISO 14882:2011、12.8 / 18

如果类定义没有显式声明一个副本分配运算符,则隐式声明一个。如果类定义声明了move构造函数或move赋值运算符,则隐式声明的副本赋值运算符将定义为Delete;否则,将其定义为默认值(8.4)。如果该类具有用户声明的副本构造函数或用户声明的析构函数,则不建议使用后一种情况。X类的隐式声明的副本赋值运算符将具有以下形式:

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

如果

  • X的每个直接基类B都有一个副本分配运算符,其参数类型为const B&,const volatile B&或B,并且
  • 对于类别类型为M(或其数组)的X的所有非静态数据成员,每个此类类别类型都有一个副本分配运算符,其参数类型为const M&,const volatile M&或M。122

否则,隐式声明的副本分配运算符将具有以下形式:

X&X :: operator = {X&)

12.8 / 18上的注释122

隐式声明的副本分配运算符的引用参数不能绑定到易失性左值;参见C.1.9

另一个答案中,我引用了C.1.9:

隐式声明的副本构造函数和隐式声明的副本赋值运算符不能创建volatile左值的副本。[...]

结果是,如果我们想要一个副本赋值运算符,我们将不得不提供一个合适的副本赋值运算符。

X& operator= (X const volatile & xo);
Run Code Online (Sandbox Code Playgroud)

还要注意,您不能从volatile显式默认设置中声明分配/构造函数。

C ++ 11标准8.4.2 / 1

明确默认的功能应

  • 是一个特殊的成员函数,
  • 具有相同的声明函数类型(除了可能不同的ref限定符,以及对于复制构造函数或复制赋值运算符而言,参数类型可以是“对非常量T的引用”,其中T是成员函数的类),就好像它已隐式声明一样,并且
  • 没有默认参数。

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

[注意:这意味着参数类型,返回类型和cv限定符必须与假设的隐式声明匹配。—尾注]

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

  • 因此,要解决此问题,只是...? (3认同)