Конечно, ответ «нет», потому что люди, которые написали это, очень серьезно думали об этом, однако я хочу знать, почему.
Учитывая, что (без шаблонов) классы часто объявляются в заголовочных файлах, которые затем включаются в несколько файлов, которые компилируются отдельно, рассмотрим эти два файла:
#include <cstddef>
struct Foo {
public:
int pub;
private:
int priv;
};
size_t getsize1(Foo const &foo) {
return sizeof(foo);
}
#include <cstddef>
struct Foo {
public:
int pub;
private:
int priv;
};
size_t getsize2(Foo const &foo) {
return sizeof(foo);
}
В общем, Foo будет объявлен в заголовочном файле и включен в оба, но эффект будет таким, как показано выше. (То есть включение заголовка не является волшебством, оно просто помещает содержимое заголовков в эту строку.) Мы можем скомпилировать оба и связать их со следующим:
#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
Но гарантирует ли это стандарт c ++ 11? действительно Можно ожидать совместимости макета со всеми 3 Foo? Foo содержит как закрытые, так и открытые поля, следовательно, это не структура стандартного макета, как определено в п. 9, п. 7 стандарта c ++ 11 (рабочий проект):
Класс стандартного макета — это класс, который:
- не имеет нестатических членов-данных типа нестандартного класса макета (или массива таких типов) или ссылки,
- не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1),
- имеет одинаковый контроль доступа (пункт 11) для всех нестатических элементов данных,
- не имеет базовых классов нестандартной компоновки,
- либо не имеет нестатических членов данных в самом производном классе и не более одного базового класса с нестатическими членами данных, или не имеет базовых классов с нестатическими членами данных, и
- не имеет базовых классов того же типа, что и первый нестатический элемент данных.
Поскольку мы используем структуры, и чтобы быть тщательным, следующий пункт говорит:
Структура стандартного макета — это класс стандартного макета, определенный структурой ключ-класс или класс-ключ. Объединение стандартного макета — это класс стандартного макета, определенный с помощью объединения ключ-класс.
Насколько мне известно, стандарт определяет совместимость макетов только между структурами в стандартном макете (пункт 9.2, п. 18).
Два типа структуры стандартного макета (раздел 9) совместимы с макетом, если они имеют одинаковое количество элементов нестатических данных, а соответствующие элементы нестатических данных (в порядке объявления) имеют типы, совместимые с макетом (3.9).
Так гарантируется ли, что все три Foo совместимы с макетом, и что более важно, почему?
Почему (недетерминированный) компилятор, который создает различные компоновки для Foo во время компиляции, не был бы компилятором c ++ 11?
Три Foo
s совместимы с макетом, потому что они того же типа, struct ::Foo
,
[basic.types]
11 — Если два типа T1 и T2 имеют одинаковый тип, то T1 и T2 являются типами, совместимыми с макетом.
Классы имеют одинаковый тип, потому что они имеют одинаковое (полностью определенное) имя и имеют внешнюю связь:
[Основная]
9 — имя, используемое в нескольких единицах перевода, потенциально может относиться к одному и тому же объекту в этих единицах перевода в зависимости от связи (3.5) имени, указанного в каждой единице перевода.
Имена классов, объявленные в области имен, которые не объявлены (рекурсивно) в безымянном пространстве имен, имеют внешнюю связь:
[Basic.link]
2 — Говорят, что имя имеет связь, когда оно может обозначать тот же […] тип […], что и имя, введенное объявлением в другой области видимости:
— Когда имя имеет внешнюю связь, обозначаемая им сущность может именоваться именами из областей действия других единиц перевода или из других областей той же единицы перевода. […] 4 — безымянное пространство имен или пространство имен, объявленное прямо или косвенно в безымянном пространстве имен, имеет внутреннюю связь. Все остальные пространства имен имеют внешнюю связь. Имя, имеющее область пространства имен, которому не была дана внутренняя связь выше, имеет ту же связь, что и окружающее пространство имен, если это имя […] — именованный класс (раздел 9) или безымянный класс, определенный в объявлении typedef, в котором класс имеет имя typedef для целей связывания (7.1.3) […]
Обратите внимание, что разрешено иметь несколько определений типа класса в разных единицах перевода, если определения состоят из одной и той же последовательности токенов:
[Basic.def.odr]
6 — В программе может быть несколько определений типа класса (раздел 9) […] при условии, что каждое определение отображается в разных единицах перевода, и при условии […] каждого определения […] должны состоять из одинаковой последовательности токенов […]
Так что если Foo
у них разные имена, они не будут одного типа; если они появились в анонимном пространстве имен или в определении функции (Кроме встроенная функция; увидеть [Dcl.fct.spec]/ 4) они не будут иметь внешних связей и поэтому не будут однотипными. В любом случае они были бы совместимы с макетом, только если бы они были стандартным макетом.
Некоторые примеры:
// tu1.cpp
struct Foo { private: int i; public: int j; };
// tu2.cpp
struct Foo { private: int i; public: int j; };
Два Foo
с того же типа.
// tu1.cpp
struct Foo { private: int i; public: int j; };
// tu2.cpp
struct Foo { private: int i; public: int k; };
Нарушение ODR; неопределенное поведение.
// tu1.cpp
struct Foo { private: int i; public: int j; };
// tu2.cpp
struct Bar { private: int i; public: int j; };
Разные имена, разные типы. Не совместим с макетом.
// tu1.cpp
struct Foo { int i; int j; };
// tu2.cpp
struct Bar { int i; int j; };
Разные имена, разные типы, но совместимые с макетом (начиная со стандартного макета).
// tu1.cpp
namespace { struct Foo { private: int i; public: int j; }; }
// tu2.cpp
namespace { struct Foo { private: int i; public: int j; }; }
Внутренняя связь; Различные типы.
// tu1.cpp
static void f() { struct Foo { private: int i; public: int j; }; }
// tu2.cpp
static void f() { struct Foo { private: int i; public: int j; }; }
Нет связи; Различные типы.
// tu1.cpp
inline void f() { struct Foo { private: int i; public: int j; }; }
// tu2.cpp
inline void f() { struct Foo { private: int i; public: int j; }; }
Один и тот же тип [Dcl.fct.spec]/ 4.