从cppreference考虑这个例子:
struct S { static const int x = 1; };
void f() { &S::x; } // discarded-value expression does not odr-use S::x
Run Code Online (Sandbox Code Playgroud)
我同意这&S::x
是一个废弃值表达式,因为标准说(9.2,第1段[stmt.expr]来自n4700)
表达式语句具有表单
Run Code Online (Sandbox Code Playgroud)expression-statement: expression_opt ;
表达式是一个废弃值表达式(第8条)......
但是,这是否足够S::x
不被使用?6.2,第3段[basic.def.odr]指出
一种可变
x
为潜在评估表达式出现的名字ex
被ODR使用的由ex
除非
- ...
- if
x
是一个对象,ex
是表达式的潜在结果集的一个元素e
,其中任何一个
- 左值到左值的转换(7.1)适用于
e
或e
是丢弃值表达式(第8条).
问题是丢弃值表达式&S::x
没有潜在的结果(这意味着它S::x
不是潜在的结果&S::x
),如6.2,第2段[basic.def.odr]所示:
...表达式的潜在结果集
e
定义如下:
- 如果
e
是id-expression(8.1.4),则该集仅包含e
. …
根据N4606,4.5 [conv.qual]第3段读到
如果满足以下条件,则
T1
可以将类型的prvalue表达式转换为类型T2
,其中cv i j表示T j的cv限定签名中的cv限定符:
- ...
- 如果cv 1 i和cv 2 i不同,那么对于0 <k <i ,const在每个cv 2 k中.
上面的最后一个子弹表明以下转换失败.
T1 : pointer to / pointer to / pointer to / T
T2 : pointer to / pointer to / const pointer to / T
Run Code Online (Sandbox Code Playgroud)
为了成功,T2
必须pointer to / const pointer to / const pointer to / T
.是不是T2
足够的只是更加CV-资格比T1
?为什么转换成功所需的更低维度的cv-qualifiers更多?
b
此代码中的表达式应为核心常量表达式
int main()
{
constexpr int a = 10;
const int &b = a;
constexpr int c = b; // here
return 0;
}
Run Code Online (Sandbox Code Playgroud)
由于标准说(8.20,第2段[expr.const]在N4700)
表达式
e
是核心常量表达式,除非评估e
将评估以下表达式之一:
...
除非适用,否则左值 - 右值转换(7.1)
...
非易失性glvalue,引用constexpr定义的非易失性对象,或引用此类对象的不可变子对象,或者
首先,b
上面代码中的表达式是一个左值(也是一个glvalue),因为它是一个引用,因此是一个变量(8.1.4.1,第1段[expr.prim.id.unqual]):
如果实体是函数,变量或数据成员,则表达式是左值 ,否则为prvalue; 如果标识符指定位字段(11.5),则它是位字段.
其次,变量b
表示的对象是a
,并且声明了它constexpr
.但是,gcc抱怨道
./hello.cpp: In function ‘int main()’:
./hello.cpp:6:20: error: the value of ‘b’ is not usable in a constant expression
constexpr int …
Run Code Online (Sandbox Code Playgroud) 根据标准(§3.3.3/ 4),N4567的标准禁止某些类型的重新声明先前在条件中声明的名称 - 如下:
在for-init-statement,for-range-declaration以及if,while,for和switch语句的条件中声明的名称是if,while,for或switch语句(包括受控语句)的本地名称,并且不得在该陈述的后续条件下,也不得在受控陈述的最外层(或if语句,任何最外层)中重新宣布; 见6.4.
但是,考虑到以下代码编译正常的事实,
int main(void) {
if (int i=10)
if (int i=20)
;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我似乎不清楚究竟"该陈述的后续条件"是什么代表什么.
似乎有在没有明确的定义声明的点,声明性区域,范围的一个命名空间标识符,除了那些的一个命名空间中的标识符 -根据标准(§3.3.6/ 1).
命名空间定义的声明性区域是其namespace-body.原始命名空间名称表示的潜在范围是由同一个命名空间定义建立的声明性区域的串联...
虽然标准确实讨论了声明的那些- 命名空间定义是声明,但是它不适用于命名空间定义的情况,因为它没有声明符,也没有初始值 -根据标准(§3.3.2/ 1) .
声明的名称是在完整的声明者(第8条)之后和初始化者(如果有的话)之后,除非如下所述......
然后,我如何确定命名空间标识符的那些?
请考虑C++模板中的引用:完整指南(第2版):
Run Code Online (Sandbox Code Playgroud)decltype(auto) ret{std::invoke(std::forward<Callable>(op), std::forward<Args>(args)...)}; ... return ret;
需要注意的是,宣布
ret
与auto&&
不正确.作为引用,auto&&
将返回值的生存期延长到其作用域的末尾,但不超出return
对函数调用者的语句.
作者说这auto&&
不适合完美转发回报值.但是,是否也没有decltype(auto)
形成对xvalue/lvalue的引用?国际海事组织,decltype(auto)
然后遭受同样的问题.那么,作者有什么意义呢?
编辑:
上面的代码片段应该放在这个函数模板中.
template<typename Callable, typename... Args>
decltype(auto) call(Callable&& op, Args&&... args) {
// here
}
Run Code Online (Sandbox Code Playgroud) 我一直在想为什么constexr
和virtual
互相排斥,有人补充道:
... constexpr是关于编译时的执行; 如果我们在编译时执行该函数,我们显然也知道它在编译时所处理的数据类型,因此后期绑定显然不相关.
但是,即使在编译时,动态类型也可能与静态类型不同,并且可能存在需要动态类型的情况:
class A {
public:
/* virtual */ constexpr int foo() {
return 1;
}
};
class B : public A {
public:
constexpr int foo() {
return 2;
}
};
constexpr int foo(A &a) {
// The static type is fixed here.
// What if we want to call B::foo() ?
return a.foo();
}
int main() {
B b;
constexpr int c = foo(b);
return …
Run Code Online (Sandbox Code Playgroud) 我编译了代码,
#include <stdio.h>
struct s {
int a : 6;
_Bool b : 1;
_Bool c : 1;
_Bool d : 1;
_Bool e : 1;
_Bool f : 1;
_Bool g : 1;
int h : 12;
};
void main(void) {
printf("%d\n", sizeof(struct s));
}
Run Code Online (Sandbox Code Playgroud)
并且输出有点出乎意料.
12
Run Code Online (Sandbox Code Playgroud)
如C11草案所述,
...如果剩余足够的空间,紧跟在结构中另一个位域之后的位域应被打包到同一单元的相邻位中......
因为我使用了32位编译器,所以我预计它会适合4个字节.具体来说,我使用了gcc(tdm-1)5.1.0.这是违反标准的吗?
编辑:
将所有的_Bool
s 替换int
为预期的作品......我不确定为什么......
编辑:
在gcc 5.4.0中,代码按预期工作.这个问题的关键点在于为什么尾随_Bool
s和int
不适合第一个.我想我没有做太多关于实现的假设(除了int
至少4个字节,这是可以接受的),我在这里谈论C标准的C保证行为.因此,我不能同意下面的一些评论.
我在最新的C++标准草案(N4606)中遇到过类型"cv void":
8.3.3 [dcl.mptr],第3段
指向成员的指针不应指向类(9.2.3)的静态成员,具有引用类型的成员或"cv void".
通过一些研究,我发现"cv void"是一个真实的类型,但我不知道与void类型相比有什么区别.你能用一个例子(可能带代码)解释一下吗?
编辑:
3.9.1 [basic.fundamental],第9段
类型cv void是不完整的类型,无法完成; 这样的类型有一组空值...
考虑来自7.6.1.10第3段[expr.const.cast](N4810)的示例:
typedef int *A[3]; // array of 3 pointer to int
typedef const int *const CA[3]; // array of 3 const pointer to const int
...
A &&r2 = const_cast<A&&>(CA{}); // OK
Run Code Online (Sandbox Code Playgroud)
因此,该标准说这是合法代码,但是
没有一个g++-7
或clang-6
编译正常。
事实上,这种const_cast
产权相关评论从llvm
状态:
....
if (isa<RValueReferenceType>(DestTypeTmp) && SrcExpr.get()->isRValue()) {
if (!SrcType->isRecordType()) {
// Cannot const_cast non-class prvalue to rvalue reference type. But if
// this is C-style, static_cast can do this.
msg …
Run Code Online (Sandbox Code Playgroud)