Is the layout compatibility in the C ++ 11 standard (working draft) too weak?

Of course, the answer is no, because the people who wrote it thought it very hard, but I want to know why.

Given that classes (without templates) are often declared in header files, which are then included in several files that are compiled separately, re-name these two files:

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); } 

In general, Foo will be declared in the header file and included in both, but the effect is as shown above. (That is, including the title is not magic, it simply puts the contents of the headers on this line.) We can compile both and associate them with the following:

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'; } 

One way to do this using 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 

And (in my architecture and environment), this shows: 8, 8, 8. sizeof are the same for each compilation file1.cc, file2.cc and main.cc

But does this guarantee the C ++ 11 standard, is it really normal to have compatibility with all 3 Foo? Foo contains both private and public fields, therefore, this is not a standard layout structure, as defined in clause 9 clause 7 of the C ++ 11 standard (working draft):

A standard layout class is a class that:

  • does not have non-static data members such as a non-standard class layout (or an array of such types) or a link,
  • does not have virtual functions (10.3) and there are no virtual base classes (10.1),
  • has the same access control (section 11) for all non-static data members ,
  • does not have base classes of non-standard layout,
  • either does not have non-static data members in the derived class and no more than one base class with non-static data members, or does not have base classes with non-static data members, and
  • does not have base classes of the same type as the first non-static data element.

Because we use structs and, to be thorough, the following par says:

A standard layout structure is a standard layout class defined using a class structure or class of a class. A standard layout layout is a standard layout class defined by combining class classes.

As far as I know, the standard defines only layout compatibility between structures in the standard layout (clause 9.2, par. 18).

Two types of standard layout structures (Section 9) are compatible with layouts if they have the same number of non-static data elements and the corresponding non-static data members (in the declaration order) have types compatible with the layout (3.9).

Thus, it is guaranteed that all three Foo are compatible with layouts, and more importantly, why?

Why not a (non-deterministic) compiler that creates different layouts for Foo at compile time, rather than a C ++ 11 compiler?

+6
source share
1 answer

Three Foo compatible with layouts because they are of the same type, struct ::Foo .

[basic.types]

11 - If two types T1 and T2 are of the same type, then T1 and T2 are layout compatible types.

Classes are of the same type because they have the same (fully qualified) name and have an external connection:

[main]

9 - The name used in several translation units may potentially refer to the same object in these translation units depending on the binding (3.5) of the name indicated in each translation unit.

Class names declared in a namespace scope that are not declared (recursively) in an unnamed namespace have an external reference:

[basic.link]

2 - It is said that a name has a connection when it can denote the same type [...] [...] as the name entered by the declaration in another area:
- When a name has an external reference, the entity that it denotes can be called names from the areas of other translation units or from other areas of the same translation unit. [...]
4 - An unnamed namespace or namespace declared directly or indirectly in an unnamed namespace has an internal relationship. All other namespaces have an external connection. A name with a namespace scope that was not specified in the internal link above has the same relationship as the encompassing namespace if that name [...]
- the named class (section 9) or an unnamed class defined in the typedef declaration, in which the class has the name typedef for binding purposes (7.1.3) [...]

Please note that it is allowed to have several class type definitions displayed in different translation units if the definitions consist of the same sequence of tokens:

[basic.def.odr]

6 - The program may have more than one definition of the type of class (section 9) [...], provided that each definition appears in a different translation unit and provides [...] each definition [...] must consist of one and the same sequence of tokens [...]

So, if Foo had different names, they would not be of the same type; if they appeared in an anonymous namespace or in a function definition (except for the built-in function, see [dcl.fct.spec] / 4), they would not have an external connection and therefore would not be of the same type, In any case, they would compatible with layouts only if they were standard.


Some examples:

 // tu1.cpp struct Foo { private: int i; public: int j; }; // tu2.cpp struct Foo { private: int i; public: int j; }; 

Two Foo are the same type.

 // tu1.cpp struct Foo { private: int i; public: int j; }; // tu2.cpp struct Foo { private: int i; public: int k; }; 

ODR violation; undefined.

 // tu1.cpp struct Foo { private: int i; public: int j; }; // tu2.cpp struct Bar { private: int i; public: int j; }; 

Different names, therefore different types. Not compatible with layouts.

 // tu1.cpp struct Foo { int i; int j; }; // tu2.cpp struct Bar { int i; int j; }; 

Different names, different types, but compatible with layouts (from a standard layout).

 // tu1.cpp namespace { struct Foo { private: int i; public: int j; }; } // tu2.cpp namespace { struct Foo { private: int i; public: int j; }; } 

Internal communication; different types.

 // 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; }; } 

No connection; different types.

 // 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; }; } 

The same type: [dcl.fct.spec] / 4.

+13
source

Source: https://habr.com/ru/post/977130/


All Articles