Совместимость макета в стандарте c ++ 11 (рабочий проект) слишком слабая?

Конечно, ответ «нет», потому что люди, которые написали это, очень серьезно думали об этом, однако я хочу знать, почему.

Учитывая, что (без шаблонов) классы часто объявляются в заголовочных файлах, которые затем включаются в несколько файлов, которые компилируются отдельно, рассмотрим эти два файла:

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

Но гарантирует ли это стандарт c ++ 11? действительно Можно ожидать совместимости макета со всеми 3 Foo? Foo содержит как закрытые, так и открытые поля, следовательно, это не структура стандартного макета, как определено в п. 9, п. 7 стандарта c ++ 11 (рабочий проект):

Класс стандартного макета — это класс, который:

  • не имеет нестатических членов-данных типа нестандартного класса макета (или массива таких типов) или ссылки,
  • не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1),
  • имеет одинаковый контроль доступа (пункт 11) для всех нестатических элементов данных,
  • не имеет базовых классов нестандартной компоновки,
  • либо не имеет нестатических членов данных в самом производном классе и не более одного базового класса с нестатическими членами данных, или не имеет базовых классов с нестатическими членами данных, и
  • не имеет базовых классов того же типа, что и первый нестатический элемент данных.

Поскольку мы используем структуры, и чтобы быть тщательным, следующий пункт говорит:

Структура стандартного макета — это класс стандартного макета, определенный структурой ключ-класс или класс-ключ. Объединение стандартного макета — это класс стандартного макета, определенный с помощью объединения ключ-класс.

Насколько мне известно, стандарт определяет совместимость макетов только между структурами в стандартном макете (пункт 9.2, п. 18).

Два типа структуры стандартного макета (раздел 9) совместимы с макетом, если они имеют одинаковое количество элементов нестатических данных, а соответствующие элементы нестатических данных (в порядке объявления) имеют типы, совместимые с макетом (3.9).

Так гарантируется ли, что все три Foo совместимы с макетом, и что более важно, почему?

Почему (недетерминированный) компилятор, который создает различные компоновки для Foo во время компиляции, не был бы компилятором c ++ 11?

6

Решение

Три Foos совместимы с макетом, потому что они того же типа, 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.

13

Другие решения


По вопросам рекламы [email protected]