考虑以下陈述
volatile int a = 7;
a; // statement A
volatile int* b = &a;
*b; // statement B
volatile int& c = a;
c; // statement C
现在,我一直试图在标准中找到一个点,它告诉我编译器在遇到这些语句时的行为方式。我能找到的只是A(可能是C)给了我一个左值,B也是如此。
标识符是id-expression,只要它已被适当声明(第7条)。 [..]
[..]结果是由标识符表示的实体。结果是 如果实体是函数,变量或数据成员,则左值 否则为prvalue [..]
一元*运算符执行间接:它所应用的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是一个左值,引用表达式指向的对象或函数
我用clang ++ 3.2-11和g ++ 4.7.3尝试了这个,第一个用C ++ 11模式产生三个读取,在C ++ 03模式下产生零读取(输出三个警告),而g ++只产生前两个,明确警告我不会生成第三个。
很明显,从标准中的引用行中,哪种类型的值来自表达式,但是:
哪些语句(A,B,C)应根据C ++标准从易失实体中产生读数?
答案 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),
这对我来说似乎含糊不清,因为关于5
和a;
部分c;
表示它是5.1.1 p8
,对我而言,它涵盖了这个案例并不明显但正如乔纳森发现DR 1054说它确实涵盖了这种情况。