#опытным
Опытные читатели могли заметить кое-что странное в этом посте. И заметили кстати. Изначально cin, cout и тд определены, как простые массивы. А в iostream они уже становятся объектами потоков и линкуются как онные. То есть в одной единице трансляции
extern std::ostream cout;
extern std::istream cin;
...
А в другой
// Standard stream objects.
// NB: Iff <iostream> is included, these definitions become wonky.
typedef char fake_istream[sizeof(istream)]
__attribute__ ((aligned(__alignof__(istream))));
typedef char fake_ostream[sizeof(ostream)]
__attribute__ ((aligned(__alignof__(ostream))));
fake_istream cin;
fake_ostream cout;
fake_ostream cerr;
fake_ostream clog;
Что за приколы такие? Почему массивы нормально линкуются на объекты кастомных классов?
В С++ кстати запрещены такие фокусы. Типы объявления и определения сущности должны совпадать.
Все потому что линкер особо не заботится о типах, выравнивании и даже особо о размерах объектов. То есть я буквально могу прилинковать объект одного кастомного класса к другому и мне никто никакого предупреждения не влепит. То есть такой код вполне нормально компилится:
// header.hpp
#pragma once
struct TwoFields {
int a;
int b;
};
struct ThreeFields {
char a;
int b;
long long c;
};
// source.cpp
ThreeFields test = {1, 2, 3};
// main.cpp
#include <iostream>
#include "header.hpp"
extern TwoFields test;
int main() {
std::cout << test.a << " " << test.b << std::endl;
}
На консоли появится "1 2". Но ни типы, ни размеры типов, ни выравнивания у объектов из объявления и определения не совпадают. Поэтому здесь явное UB.
Но в исходниках GCC так удачно сложилось, что массивы реально представляют собой идеальные сосуды для объектов io-потоков. На них даже сконструировали реальные объекты. Поэтому такие массивы можно интерпретировать как сами объекты.
Это, естественно, все непереносимо. Но поговорка "спички детям - не игрушка" подходит только для тех, кто плохо понимает, что делает. А разработчики компилятора не из этих ребят.
Take conscious risks. Stay cool.
#cppcore #compiler