Yes it is allowed, modulo bugs in GCC.
The compiler
GCC follows the Itanium ABI, which is actually platform-independent, despite the name. Here is the Itanium ABI mission statement:
we want users to be able to build relocatable objects with different compilers and link them together, and if possible even to ship common DSOs.
Note there are no separate ABI specifications for separate versions of the C++ standard. There is one specification that works for them all.
The library
Here is the mission statement of libstdc++ as far as versioning is concerned
Extending existing, stable ABIs. Versioning gives subsequent releases of library binaries the ability to add new symbols and add functionality, all the while retaining compatibility with the previous releases in the series. Thus, program binaries linked with the initial release of a library binary will still run correctly if the library binary is replaced by carefully-managed subsequent library binaries. This is called forward compatibility.
The library supports not one, but two different ABIs. There was a change in the C++11 standard that necessitated an ABI split. However, as the documentation points out, the choice of ABI to use is independent of the -std option used to compile your code. This is ancient history however. You are going to use the new C++11-compatible ABI, which is the default, about 100% of the time, unless you need to maintain an old piece of software built with pre-C++11-compatible ABI.
The real life
The open source ecosystem has zillions of C++ libraries that are used in all kind of products. No one coordinates -std option between maintainers of different libraries. Everybody upstream uses what they want/need, and downstream the libraries are built with whatever options are there, and linked together with no problem. It all just works.
I personally run Gentoo, which is a rolling release distro. I fetch whatever stable release of a software component is available directly from that library's GitHub or whatever it is stored, and compile with whatever compiler version I currently have. I can recompile any library using any compiler version at any time. The system still works just fine. Without this kind of cross-standard, cross-version compatibility, a rolling release would never ever have a chance to work.
Conclusion
Is it 100% safe? You decide. There are compiler bugs in this area (you have found one) and sometimes people get biten by them. Then again, there are compiler bugs in all areas, but people still use compilers.
Answer from n. m. could be an AI on Stack Overflowc++ - Is mixing C++11 and C++20 allowed with GCC? - Stack Overflow
Which c++ standard to adopt for new projects, c++11 to c++20?
When is stable GCC c++20 support expected?
Why major compilers likw GCC and Clang still do not support C++20 modules ??
Videos
Yes it is allowed, modulo bugs in GCC.
The compiler
GCC follows the Itanium ABI, which is actually platform-independent, despite the name. Here is the Itanium ABI mission statement:
we want users to be able to build relocatable objects with different compilers and link them together, and if possible even to ship common DSOs.
Note there are no separate ABI specifications for separate versions of the C++ standard. There is one specification that works for them all.
The library
Here is the mission statement of libstdc++ as far as versioning is concerned
Extending existing, stable ABIs. Versioning gives subsequent releases of library binaries the ability to add new symbols and add functionality, all the while retaining compatibility with the previous releases in the series. Thus, program binaries linked with the initial release of a library binary will still run correctly if the library binary is replaced by carefully-managed subsequent library binaries. This is called forward compatibility.
The library supports not one, but two different ABIs. There was a change in the C++11 standard that necessitated an ABI split. However, as the documentation points out, the choice of ABI to use is independent of the -std option used to compile your code. This is ancient history however. You are going to use the new C++11-compatible ABI, which is the default, about 100% of the time, unless you need to maintain an old piece of software built with pre-C++11-compatible ABI.
The real life
The open source ecosystem has zillions of C++ libraries that are used in all kind of products. No one coordinates -std option between maintainers of different libraries. Everybody upstream uses what they want/need, and downstream the libraries are built with whatever options are there, and linked together with no problem. It all just works.
I personally run Gentoo, which is a rolling release distro. I fetch whatever stable release of a software component is available directly from that library's GitHub or whatever it is stored, and compile with whatever compiler version I currently have. I can recompile any library using any compiler version at any time. The system still works just fine. Without this kind of cross-standard, cross-version compatibility, a rolling release would never ever have a chance to work.
Conclusion
Is it 100% safe? You decide. There are compiler bugs in this area (you have found one) and sometimes people get biten by them. Then again, there are compiler bugs in all areas, but people still use compilers.
The issue can be reproduced with this small code:
#include <iostream>
struct A {
double d{0.0};
int i1{1};
};
struct C : public A {
int i2{2};
};
int main(int argc, char* argv[]) {
C c;
std::cout << "sizeof(C) = " << sizeof(C) << std::endl;
<< "&i2 - &i1 = " << (((char*) &c.i2) - ((char*) &c.i1))
<< std::endl;
return 0;
}
When compiled with G++ in C++11 mode or when compiled with clang (any mode), the result is:
sizeof(C) = 16
&i2 - &i1 = 4
When compiled with G++ in C++14 (or later) mode, the result is:
sizeof(C) = 24
&i2 - &i1 = 8
Apparently, G++ since C++14 refuses to overlay fields of struct C into struct A. Note that sizeof(A) is 16 with all compilers, which is required, so that arrays of objects of type A are well-aligned. So there is an implicit 4-byte padidng after i1. clang (all versions) and G++ (until C++11) recycle that padding, when another int is added through inheritance, G++ (later versions) does not.
This is clearly a binary compatibility breaking change between C++11 and C++14 mode in G++, that can manifest itself in many places of custom code, so linking mixed-compiled code is very dangerous, if the oldest version used is C++11.
Actually, the G++ team seems to have acknowledged this as a bug and fixed it for G++12 and later versions, but has not backported it to older versions of the compiler, so this issue is still present in G++11, which is for example the default compiler in Ubuntu 22.04 LTS. Here a link to the GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103681 Thanks to https://stackoverflow.com/users/5293624/kjpus for reporting this link.
As can be easily verified on Godbolt, the bug is present in G++ version 11.4, but fixed from version 12.1 on: https://godbolt.org/z/chnss5bs3
When starting a new C++ project, which standard should we choose? Should we go with the latest 20, or stick with earlier versions like C++11, C++14 or C++17? Is using the latest standard always the best choice? What factors should be considered when choosing a standard, such as project requirements, compiler support, and team expertise?
My group is thinking about moving compiler versions and it would be useful to know if we should wait for c++20. When do we think GCC will have non-experimental support for it?
It is 2024 and my favorite compilers (gcc and clang) still do not support modules completely !!
We already have C++23 and some new features for C++26, but still missed C++ modules ...
Maybe committee should postpone standardization ?!