考虑到(没有模板的)类经常在头文件中声明,然后被包含在编译成多个文件的头文件中,进一步考虑这两个文件:
file1.c
#include <cstddef>
struct Foo {
public:
int pub;
private:
int priv;
};
size_t getsize1(Foo const &foo) {
return sizeof(foo);
}
file2.c
#include <cstddef>
struct Foo {
public:
int pub;
private:
int priv;
};
size_t getsize2(Foo const &foo) {
return sizeof(foo);
}
通常情况下,Foo会在头文件中声明并被包含在两个文件中,但效果如上所示。(也就是说,包含头文件并不是什么魔法,它只是把头文件的内容放在那一行上。)我们可以编译这两个文件并将它们链接到以下文件:
main.cc
#include <iostream>
struct Foo {
public:
int pub;
private:
int priv;
};
size_t getsize1(Foo const &);
size_t getsize2(Foo const &);
int main() {
Foo foo;
std::cout << getsize1(foo) << ", " << getsize2(foo) << ", " << sizeof(foo) << '\n';
}
其中一种方法是使用g++:
g++ -std=c++11 -c -Wall file1.cc
g++ -std=c++11 -c -Wall file2.cc
g++ -std=c++11 -c -Wall main.cc
g++ -std=c++11 -Wall *.o -o main
在我的架构和环境下,这显示为: 8, 8, 8。对于file1.cc、file2.cc和main.cc的每次编译,sizeof都是相同的。但是,c++11标准是否保证了这一点?期望所有3个Foo具有布局兼容性真的可以吗?Foo包含私有和公共字段,因此它不是标准布局结构,如c++11标准(工作草案)第9段第7款所定义的那样:
“标准布局类是指一个类: - 没有非静态数据成员是非标准布局类的类型(或该类型的数组)或引用类型, - 没有虚函数(10.3)和没有虚基类(10.1), - 所有非静态数据成员具有相同的访问控制(第11条), - 没有非标准布局的基类, - 要么在最派生类中没有非静态数据成员,在最多一个带有非静态数据成员的基类中只有一个非静态数据成员,要么没有带有非静态数据成员的基类 - 第一个非静态数据成员没有与其类型相同的基类。”
由于我们使用的是structs,并且为了详尽起见,下一段说:
“标准布局结构体是使用class-key struct或class-key class定义的标准布局类。标准布局联合是使用class-key union定义的标准布局类。”
据我所知,标准仅在标准布局结构体之间定义了布局兼容性(第9.2段,第18款)。
“如果它们具有相同数量的非静态数据成员,并且相应的非静态数据成员(按声明顺序)具有布局兼容的类型(3.9),则两个标准布局结构体(第9条)类型具有布局兼容性。”
因此,是否保证所有三个Foo具有布局兼容性,更重要的是为什么?
一个(非确定性的)编译器为什么会在编译期间为Foo创建不同的布局,而又不是c++11编译器?
Foo
的所有三个定义都指的是同一个实体,在所有翻译单元中必须具有相同的属性集。为此,所有定义必须在标记上完全相同,并且这些标记必须具有相同的含义;否则,程序将是不合法的,无需进行任何诊断。有关详细信息,请参见 3.2/5。 - Igor Tandetnik