volatile类型的丢弃值表达式与volatile内置类型

时间:2018-03-04 11:26:43

标签: c++ language-lawyer volatile

考虑以下这段代码:

struct S{
  int i;
  S(int);
  S(const volatile S&);
  };

struct S_bad{
  int i;
  };

volatile S     as{0};
volatile S_bad as_bad{0};
volatile int   ai{0};

void test(){
   ai;     //(1)=> a load is always performed
   as;     //(2)=> Should call the volatile copy constructor
   as_bad; //(3)=> Should be ill-formed
   }

表达式ai;as;as_bad是废弃的值表达式,根据C ++草案标准N4659/[expr].12,我预计应用lvalue-to-rvalue在这三种情况下。对于情况(2),这应该导致调用volatile复制构造函数(S(const volatile S&)[expr]/12

  

[...]如果表达式是此可选转换后的prvalue,则应用临时实现转换([conv.rval])。 [注意:如果表达式是类类型的左值,则它必须具有易失性复制构造函数来初始化临时值,该临时值是左值到右值转换的结果对象。 - 结束说明]

因此案例(3)应该是格式错误的。

尽管如此,编译器的行为似乎很混乱:

  1. GCC

    • ai; =>加载ai;
    • 的值
    • as; =>没有代码生成,没有警告;
    • as_bad; =>加载as_bad.i
  2. Clang不会为case(2)生成负载并生成警告:表达式结果未使用;分配给变量以强制挥发性负载[-Wunused-volatile-lvalue]

    • ai; =>加载ai;
    • 的值
    • as; =>没有代码生成;警告表达结果未使用;分配给变量以强制挥发性负载[-Wunused-volatile-lvalue]
    • as_bad; =>与as;相同。
  3. MSVC在两种情况下都会执行加载。

    • ai; =>加载ai;
    • 的值
    • as; =>加载as.i(不调用volatile复制构造函数)
    • as_bad; =>加载as_bad.i
  4. 根据标准我的预期摘要:

    • ai; =>加载ai;
    • 的值
    • as; =>以S(const volatile S&)为参数调用as;
    • as_bad; =>生成编译错误

    我对标准的解释是对的吗?哪个编译器是正确的?

1 个答案:

答案 0 :(得分:2)

  1. C ++ 03说,对于表达式语句的结果,左值到左值的转换并不会发生,并且没有明确说明转换发生时副本发生了。
  2. 正如你所说,C ++ 11说,转换确实发生在易失性对象上,并且转换涉及复制以使其成为临时对象。
  3. C ++ 14只是清理措辞(以避免像b ? (x,y) : z那样愚蠢的事情,如果y没有计算),并添加关于易失性复制构造函数的说明。
  4. C ++ 17应用临时实现转换以保留先前的含义。
  5. 所以我的结论是(从C ++ 11开始)你是对的并且所有编译器都是错误的。特别是,除非您的复制构造函数读取它,否则不应发生S::i加载。实现定义的" access"当然,与什么是格式良好的问题无关;它只会影响是否实际生成ai的加载指令。问题是S_bad是一个聚合,但这是无关紧要的,因为它没有被列表初始化。