4

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
}

Gives:

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&&’

What typecast overload do I need to define for this to work?

4

3 回答 3

5

简短的回答:

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

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


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

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

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

例子:

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


带有标准引号'n东西的长答案或

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

方式 1:来自 volatile T 的用户提供的构造函数

标准,ISO 14882:2011,4/3

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

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

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

X (X const volatile & xo);

笔记:

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

这将使您的作业有效。

方式 2:来自 volatile T 的用户提供的复制赋值运算符

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

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

标准,ISO 14882:2011, 12.8/18

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

X& X::操作符=(const X&)

如果

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

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

X& X::运算符=(X&)

2018 年 12 月 8 日的注释 122

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

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

隐式声明的复制构造函数和隐式声明的复制赋值运算符不能复制 volatile 左值。[ ... ]

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

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

另请注意,您不能从 volatile 显式默认声明分配/构造函数。

C++11 标准 8.4.2/1

显式默认的函数应

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

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

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

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

于 2013-06-20T17:59:16.927 回答
1

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

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 甚至是微不足道的可复制类型。这可能会破坏使其不稳定的全部意义。

于 2013-06-20T18:04:00.740 回答
0

您可以实现赋值运算符=

T& operator=(const volatile T &rhs) {
    m_x = rhs.m_x;
    return *this;
}
于 2013-06-20T18:04:09.203 回答