代码之家  ›  专栏  ›  技术社区  ›  Oliv

volatile类类型的丢弃值表达式的行为与volatile内置类型的行为不同

  •  12
  • Oliv  · 技术社区  · 7 年前

    考虑以下代码:

    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 copy构造函数( S(const volatile S&) ) [expr]/12

    [...]如果此可选转换后表达式是prvalue,则应用临时物化转换([conv.rval])。[注意:如果表达式是类类型的左值,则必须有一个易失性副本构造函数来初始化作为左值到右值转换的结果对象的临时对象。结束注意]

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

    然而,编译器的行为似乎很混乱:

    1. 通用条款:

      • 人工智能; =>加载的值 ai ;
      • =>无代码生成,无警告;
      • as_bad; =>荷载 as_bad.i .
    2. Clang不会为情况(2)生成负载,并生成警告: 表达式结果未使用;赋给变量以强制可变负载[-Wunused volatile lvalue]

      • 人工智能; =>加载的值 人工智能 ;
      • =>未生成代码;警告 表达式结果未使用;赋给变量以强制可变负载[-Wunused volatile lvalue]
      • as\U坏; =>等同于 .
    3. MSVC在这两种情况下都执行加载。

      • 人工智能; =>加载的值 人工智能 ;
      • =>荷载 as.i (无需调用volatile copy构造函数)
      • as\U坏; =>荷载 as\U坏。我 .

    根据标准,我的期望总结:

    • 人工智能; =>加载的值 人工智能 ;
    • =>呼叫 S(常量易失性S&) 具有 as 作为论据;
    • as\U坏; =>生成编译错误

    我对标准的解释正确吗?哪种编译器是正确的(如果有的话)?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Davis Herring    7 年前
    1. C++03表示,对于表达式语句的结果,不会发生从左值到右值的转换,也没有明确表示在转换发生时会发生复制。
    2. 正如您所述,C++11表示转换确实发生在易失性对象上,并且转换涉及复制以生成临时对象。
    3. C++14只是清理了措辞(避免像这样愚蠢的事情 b ? (x,y) : z 不计算,如果 y ,并添加关于volatile copy构造函数的注释。
    4. C++17应用临时物化转换来保留之前的含义。

    因此,我的结论是(从C++11开始),您是正确的,而所有编译器都是错误的。特别是 S::i 除非复制构造函数读取,否则不应该发生加载。当然,实施定义的“访问”性质与什么是形式良好的问题无关;它只影响 ai 实际生成的。有一个问题 S_bad 是一个聚合,但这并不重要,因为它不是列表初始化的。