涉及带有volatile变量的表达式的简单语句的正确行为?

时间:2013-11-27 12:44:05

标签: c++ standards volatile language-lawyer

考虑以下陈述

volatile int a = 7;
a;   // statement A
volatile int* b = &a;
*b;  // statement B
volatile int& c = a;
c;   // statement C

现在,我一直试图在标准中找到一个点,它告诉我编译器在遇到这些语句时的行为方式。我能找到的只是A(可能是C)给了我一个左值,B也是如此。

“§5.1.1.8主要表达式 - 一般”说

  

标识符是id-expression,只要它已被适当声明(第7条)。 [..]
  [..]结果是由标识符表示的实体。结果是   如果实体是函数,变量或数据成员,则左值   否则为prvalue   [..]

“§5.3.1一元运算符”说

  

一元*运算符执行间接:它所应用的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是一个左值,引用表达式指向的对象或函数

clang和gcc

我用clang ++ 3.2-11和g ++ 4.7.3尝试了这个,第一个用C ++ 11模式产生三个读取,在C ++ 03模式下产生零读取(输出三个警告),而g ++只产生前两个,明确警告我不会生成第三个。

问题

很明显,从标准中的引用行中,哪种类型的值来自表达式,但是:
哪些语句(A,B,C)应根据C ++标准从易失实体中产生读数?

2 个答案:

答案 0 :(得分:5)

关于“隐式取消引用”的G ++警告来自gcc/cp/cvt.c中的代码,该代码故意不通过引用加载值:

    /* Don't load the value if this is an implicit dereference, or if
       the type needs to be handled by ctors/dtors.  */
    else if (is_volatile && is_reference)

G ++故意这样做,因为正如手册(When is a Volatile C++ Object Accessed?)中所述,标准不清楚什么构成了对volatile限定对象的访问。如上所述,您需要强制左值到右值转换以强制来自易失性的负载。

Clang在C ++ 03模式下发出警告,表示类似的解释:

a.cc:4:3: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
  a;   // statement A
  ^
a.cc:6:3: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
  *b;  // statement B
  ^~
a.cc:8:3: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
  c;   // statement C
  ^
3 warnings generated.

G ++行为和GCC手册似乎对C ++ 03来说是正确的,但是相对于DR 1054引入的C ++ 03,C ++ 11有所不同(这也解释了为什么是Clang在C ++)3和C ++ 11模式中表现不同。 5 [expr] p10定义了一个 abandoned-value-expression ,并表示对于volatile,左值到右值的转换 应用于 id-expression 例如你的语句A和C.左值到右值转换的规范(4.1 [conv.lval])表示结果是glvalue的值,它构成了volatile的访问。根据5p10,你的所有三个语句都应该被访问,因此G ++对语句C的处理需要更新以符合C ++ 11。我已将其报告为http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59314

答案 1 :(得分:3)

gcc文档7.1 When is a Volatile C++ Object Accessed?与此相关,我引述(强调我的前进):

  

C ++标准在处理易失性物体方面与C标准不同。 它未能指定什么构成易失性访问,除了说C ++的行为方式与C相似,相对于挥发性

     

在void上下文中访问对象时,C和C ++语言规范会有所不同:

并提供此示例:

volatile int *src = somevalue;
*src;

并继续说:

  

C ++标准指定此类表达式不进行左值转换,并且取消引用对象的类型可能不完整。 C ++标准没有明确指出它是导致访问的右值转换的左值。

应该引用标准草案部分5.3.1 一元运算符 1 ,其中说:

  

一元*运算符执行间接:它所应用的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是左值引用对象或表达式指向的函数。 [...]

关于参考文献:

  

当使用对volatile 的引用时,G ++不会将等效表达式视为对volatile的访问,而是发出不会访问volatile的警告。这样做的理由是,否则很难确定易失性访问的发生位置,并且不可能忽略返回volatile参考的函数的返回值。同样,如果您想强制阅读,请将引用转换为右值

所以看起来gcc选择不同地处理对volatile 的引用,为了强制读取你需要转换为右值,例如:

static_cast<volatile int>( c ) ;

从<{1}} 静态广告部分生成 prvalue ,从而生成左值到左值转化

  

表达式static_cast(v)的结果是将表达式v转换为类型T的结果。如果T是左值引用类型或对函数类型的右值引用,则结果为左值;如果T是对象类型的右值引用,则结果为xvalue; 否则,结果是prvalue。

更新

C++11 draft standard添加了5.2.9 表达式 11 ,其中包含:

  

在某些情况下,表达式仅出现其副作用。这种表达式称为丢弃值表达式。计算表达式并丢弃其值。不应用数组到指针(4.2)和函数指针(4.3)标准转换。 当且仅当表达式是volatile限定类型的左值并且它是下列之一时,才应用左值到右值转换(4.1):

并包括:

  

- id-expression(5.1.1),

这对我来说似乎含糊不清,因为关于5a;部分c;表示它是5.1.1 p8,对我而言,它涵盖了这个案例并不明显但正如乔纳森发现DR 1054说它确实涵盖了这种情况。