纠正涉及具有可变变量的表达式的琐碎语句的行为

Correct behaviour of trivial statements involving expressions with volatile variables?

本文关键字:表达式 语句 变量      更新时间:2023-10-16

考虑以下语句

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表达式,前提是它已经被适当地声明(第7条)。[..]
[..]结果是由标识符表示的实体。结果是如果实体是函数、变量或数据成员,并且否则为prvalue
[..]

"§5.3.1一元运算符"

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

clang和gcc

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

问题

很清楚哪种类型的值来自表达式,来自标准中引用的行,但是:
根据C++标准,哪个语句(A、B、C)应该从易失性实体产生读取

关于"隐式取消引用"的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++有意这样做,因为正如手册中所述(何时访问易失性C++对象?),该标准不清楚什么是对易失性限定对象的访问。如前所述,您需要强制进行左值到右值的转换,以强制从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定义了一个丢弃值表达式,并表示对于volatile,左值到右值的转换应用于id表达式

gcc文档7.1何时访问易失性C++对象?与此相关,我引用(强调我的未来):

C++标准与C标准在处理挥发性物质方面有所不同它没有指定什么构成易失性访问,只是说C++在易失性方面的行为应该与C类似

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

并提供了这个例子:

volatile int *src = somevalue;
*src;

并继续说道:

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

应参考标准草案5.3.1一元运算符1段,其中写道:

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

关于参考文献:

当使用对volatile的引用时,G++不会将等效表达式视为对volatiles的访问,而是发出警告,表示没有访问volatile。这样做的理由是,否则很难确定易失性访问发生在哪里,也不可能忽略返回易失性引用的函数的返回值。同样,如果您希望强制读取,请将引用强制转换为右值

因此,看起来gcc选择以不同的方式处理对volatile的引用,并且为了强制读取,您需要转换为右值,例如:

static_cast<volatile int>( c ) ;

它从5.2.9静态转换部分生成prvalue,从而生成lvalue-to-rvalue转换:

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

更新

C++11标准草案增加了5表达式11段,其中写道:

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

包括:

--id表达(5.1.1),

这对我来说似乎很模糊,因为关于a;c;5.1.1 p8节说它是lvalue,我不清楚它涵盖了这个案例,但正如Jonathan发现的,DR 1054说它确实涵盖了这个案件。