【问题标题】:Can I add an implicit conversion from a volatile T to a T?我可以添加从 volatile T 到 T 的隐式转换吗?
【发布时间】:2013-06-20 17:54:49
【问题描述】:

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

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

【问题讨论】:

  • 我不确定,但我想你写的是operator T() volatile
  • @MooingDuck:不能nv.operator=(n.operator T())吗?
  • 我认为永远不会调用operator T,因为没有理由将T 转换为T
  • @Mooing Duck:我也很确定这一点,甚至更重要的是,即使它“有效”,它也毫无意义,因为原来的 volatile 也阻止了变量的优化,不管你以后把它转换成什么。
  • @Eric 您还在等待更多信息吗?

标签: c++ casting operator-overloading


【解决方案1】:

简短的回答:

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

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


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

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

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

例子:

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


带有标准引号的长答案'n stuff or

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

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

标准,ISO 14882:2011,4/3

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

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

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

X (X const volatile & xo);

注意:

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

这将使您的任务有效。

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

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

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

标准,ISO 14882:2011, 12.8/18

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

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

如果

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

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

X& X::operator=(X&)

关于 12.8/18 的注释 122

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

other answer 中,我引用了 C.1.9,它说:

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

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

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

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

C++11 标准 8.4.2/1

一个显式默认的函数应该

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

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

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

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

【讨论】:

  • 所以要修复这个简单的...?
  • 提供一个用户定义的赋值运算符接受一个 volatile 参数?
  • @MooingDuck 唯一的方法是用户定义的转换,但如果问题是关于隐式转换,那不是我认为的“简单”。
  • 我很确定 X (X const volatile & xo); 是一个执行隐式转换的函数,这使您的整个答案非常具有误导性。
  • 大部分标准引用都是关于隐式或默认的函数,与隐式转换几乎没有任何关系。
【解决方案2】:

以下是获取允许可变源的复制构造函数和复制赋值的方法:

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

【讨论】:

  • 恐怕这是不正确的
  • 咦,怎么会这样?它不是被识别为复制 ctor 形式吗?
  • Here 是 Clang 所说的
  • 嗯,这很明确。我还在 [dcl.fct.def.default]p1 项目符号 2 中找到了标准语言。
  • @MooingDuck:恐怕这对 OP 来说还不够(他们正在尝试做作业)
【解决方案3】:

可以实现赋值运算符=

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 2011-01-01
    • 2014-04-19
    相关资源
    最近更新 更多