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

bit*_*ask 9 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
Run Code Online (Sandbox Code Playgroud)

现在,我一直试图在标准中找到一个点,告诉我编译器在遇到这些语句时的行为方式.我能找到的只是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)应该从易失性实体中产生读取?

Jon*_*ely 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)
Run Code Online (Sandbox Code Playgroud)

G ++这是否有意的,因为如手册中所述(当是易失性C++对象访问?)的标准是不清楚什么构成volatile限定对象的访问.如上所述,您需要强制左值到右值转换以强制来自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.
Run Code Online (Sandbox Code Playgroud)

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


Sha*_*our 3

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

\n
\n

C++ 标准与 C 标准的不同之处在于对易失性对象的处理。它没有指定什么构成了易失性访问,只是说 C++ 在易失性方面应该以与 C 类似的方式运行。

\n

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

\n
\n

并提供了这个例子:

\n
volatile int *src = somevalue;\n*src;\n
Run Code Online (Sandbox Code Playgroud)\n

并继续说道:

\n
\n

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

\n
\n

这应该指的是标准草案5.3.1 一元运算符第1段,其中说:

\n
\n

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

\n
\n

关于参考文献:

\n
\n

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

\n
\n

所以它看起来像是gcc选择以不同的方式对待对 volatile 的引用,并且为了强制读取,您需要转换为rvalue,例如:

\n
static_cast<volatile int>( c ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

它生成纯右值,从而生成左值到右值的转换,来自5.2.9 静态转换部分:

\n
\n

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

\n
\n

更新

\n

C ++11 标准草案添加了5 表达式第11段,其中表示:

\n
\n

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

\n
\n

包括:

\n
\n

\xe2\x80\x94 id 表达式 (5.1.1),

\n
\n

这对我来说似乎模棱两可,因为关于a;c;部分5.1.1 p8说它是一个lvalue,而且对我来说它并不明显它涵盖了这种情况,但正如乔纳森发现的那样,DR 1054说它确实涵盖了这种情况。

\n