下面的代码片段编译(演示):
struct A{ int i = 10; };
int main() {
struct A{ int i = 20; };
struct A;
struct A a;
}
Run Code Online (Sandbox Code Playgroud)
但这不是:
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
Run Code Online (Sandbox Code Playgroud)
我可以看到答案可能是标准中的这些段落:
[basic.lookup.elab]/2和[basic.scope.pdecl]/7.
但我真的不知道如何从这两段中推断出上面显示的不同行为.
需要注意的是在第一个例子中,struct A是不是第一次在声明阐述类型说明符 struct A;,但在定义struct A中main().
在第二个例子中,struct A也未先在声明阐述型说明符 struct …
既没有clang也没有g++编译这个片段:
struct A{
protected:
struct B{};
};
struct D: A::B, A{};
Run Code Online (Sandbox Code Playgroud)
根据[class.access]/7,我们有:
类似地,使用A :: B作为基本说明符是格式良好的,因为D是从A派生的,因此必须推迟检查基本说明符,直到看到整个base-specifier-list.
上面的例子和句子是[class.access]/7的一部分,至少从C++ 11开始.
请参阅[class.base.init]/11中的此示例
struct A {
A() = default; // OK
A(int v) : v(v) { } // OK
const int& v = 42; // OK
};
A a1; // error: ill-formed binding of temporary to reference
A a2(1); // OK, unfortunately
Run Code Online (Sandbox Code Playgroud)
无论铛和g ++编译代码(铛一个警告),但我想明白为什么他们打印0的成员a1.v和a2.v?见演示.
VS2015并clang编译此代码,但g++拒绝它.
namespace A {
struct B {
friend void f();
};
}
void A::f() {}
int main(){
}
Run Code Online (Sandbox Code Playgroud)
我认为g ++是正确的,因为7.3.1.2/3中的这个注释:
如果非本地类中的朋友声明首先声明了类,函数,类模板或函数模板97,则该朋友是最内部封闭命名空间的成员.友元声明本身不会使名称对非限定查找([basic.lookup.unqual])或限定查找([basic.lookup.qual])可见.[注意:如果在命名空间范围内提供匹配的声明(在类定义授予友谊之前或之后),则朋友的名称将在其命名空间中可见. - 结束注释]如果调用了友元函数或函数模板,则可以通过名称查找找到其名称,该名称查找考虑名称空间中的函数和与函数参数类型相关联的类([basic.lookup.argdep]).如果友元声明中的名称既不是限定语句也不是模板标识,并且声明是函数或详细类型说明符,则确定实体是否先前已声明的查找不应考虑最内层封闭命名空间之外的任何范围.[注意:其他形式的友元声明不能声明最内层封闭命名空间的新成员,因此遵循通常的查找规则. - 结束说明]
DR 712负责将[basic.def.odr]/2中的[basic.def.odr]/2的措辞改为当前的措辞,在[basic.def.odr] 2和3中.但我仍然试图了解改革的原因,如DR所述,如下:
712.条件表达式的整数常量操作数是"使用?"
在描述在类定义中初始化的静态数据成员时,9.2.3.2 [class.static.data]第3段说,
如果在程序中使用该成员,则该成员仍应在命名空间范围内定义...
"used"的定义见3.2 [basic.def.odr]第1段:
除非它是满足出现在常量表达式(5.20 [expr.const])和左值到右值转换的要求的对象,否则将使用 其名称显示为潜在评估表达式的对象或非重载函数( 4.1 [conv.lval])
立即应用.现在考虑以下示例:
Run Code Online (Sandbox Code Playgroud)struct S { static const int a = 1; static const int b = 2; }; int f(bool x) { return x ? S::a : S::b; }根据标准的当前措辞,此示例要求
S::a并S::b在命名空间范围内定义.这样做的原因是,根据5.16 [expr.cond]第4段,这个条件表达式的结果是一个左值,并且左值到右值的转换应用于它,而不是直接应用于对象,所以这个失败"立即申请"的要求.这是令人惊讶和不幸的,因为仅使用静态数据成员的值而不是地址.(这个问题也适用于议题696的拟议决议.)
那么,如果"立即应用要求失败,则表达式S::a和S::b在条件表达式不使用(ODR使用的),等等,各自的静态成员struct S将不需要在命名空间范围中定义.但是,这是完全DR正在说什么.我错过了什么?
构造函数没有名称.在构造函数的声明中,声明 符是ptr-declarator(parameter-declaration-clause)异常规范opt attribute-specifier-seq opt形式的函数声明符(8.3.5),其中ptr-declarator仅包含一个id-expression,可选的attribute-specifier-seq和可选的包围括号,id-expression具有以下形式之一:...
是的,这编译:
struct S{
(S)() {}
};
Run Code Online (Sandbox Code Playgroud)
但为什么允许这样做呢?
铛编译器发出警告,下面的代码片段,可以看出这里.
clang++ -std=c++14 -O0 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp:1:18: warning: braces around scalar initializer [-Wbraced-scalar-init]
void point(int = {1}, int = {2}) {}
^~~
main.cpp:1:29: warning: braces around scalar initializer [-Wbraced-scalar-init]
void point(int = {1}, int = {2}) {}
^~~
2 warnings generated.
Run Code Online (Sandbox Code Playgroud)
为什么是这样?
void point(int = {1}, int = {2}) {}
int main(){
point();
}
Run Code Online (Sandbox Code Playgroud)
据我所知,{1}并且{2}是完全根据有效的默认参数[dcl.fct.default]/1,[dcl.fct]/3和[dcl.init]/1.
[basic.link]/6(我的强调):
在块作用域中声明的函数的名称和由块作用域extern声明声明的变量的名称具有链接.
...
static void f();
static int i = 0;
void g() {
extern void f(); // internal linkage
int i; // #2 i has no linkage
{
extern void f(); // internal linkage <--
extern int i; // #3 external linkage
}
}
Run Code Online (Sandbox Code Playgroud)
[basic.link]/7:
...
namespace X {
void p() {
q(); // error: q not yet declared
extern void q(); // q is a member of namespace X <--
}
void middle() …Run Code Online (Sandbox Code Playgroud) 您将在下面找到 C++ 标准中对象的定义。
C++ 程序中的构造创建、销毁、引用、访问和操作对象。一个目的是通过一个定义(6.1)创建的,通过一个 新的表达式(8.3.4),当隐式更改联合的活动成员时 (12.3),或当创建临时对象时 (7.4, 15.2)。一个对象在其构建期间 (15.7)、整个生命周期 (6.8) 和销毁期间 (15.7) 占据一个存储区域。[ 注意:函数不是对象,无论它是否像对象那样占用存储空间。—end note ] 对象的属性是在创建对象时确定的。一个对象可以有一个名称(第 6 条)。对象具有影响其生命周期 (6.8) 的存储持续时间 (6.7)。一个对象有一个类型(6.9)。有些对象是多态的(13.3);该实现生成与每个此类对象相关联的信息,从而可以在程序执行期间确定该对象的类型。对于其他对象,用于访问它们的表达式(第 8 条)。
我有一种感觉,是的,int变量可以被视为 C++ 中的对象,尽管在上面的段落中有这样的声明:
一个对象在其构建期间 (15.7)、整个生命周期 (6.8) 和销毁期间 (15.7) 占据一个存储区域。
标准中还有其他几个声明似乎假定术语对象仅限于类对象。因此,为了精确起见,我发布了这个问题。
c++ ×10
c++17 ×4
c++11 ×1
clang ×1
declaration ×1
definition ×1
friend ×1
linkage ×1
name-lookup ×1
namespaces ×1