вторник, 5 августа 2025 г.

[prog.c++] Forward declarations для типов хорошо бы держать под контролем и в одном месте

С давних пор с настороженностью отношусь к опережающим объявления (forward declarations) типов в коде. Когда вижу что-то вроде:

class A;
class B;
class C {
  const A * _a;
  B & _b;
  ...
};

то невольно начинаю ждать неприятных приключений. Коих, по моим наблюдениям, бывает два вида.

Во-первых, некоторые компиляторы (не будем показывать пальцем в сторону VC++) различают:

class A;

и

struct A;

Поэтому если в каком-то заголовочном файле есть что-то вроде:

class A;
void f(const A &);

А затем в файле реализации:

struct A { ... };
void f(const A & a) { ... };

то при линковке нужная версия f может и не обнаружиться.

Во-вторых, может оказаться, что где-то осталось:

class A;

Тогда как основное определение A было преобразовано в:

template<typename T>
class SomeTrickyTemplate { ... };

using A = SomeTrickyTemplate<int>;

И мы опять же получим приключения при линковке.


Конечно же хорошо, что все сломается при линковке и нам не придется разбираться с непонятным поведением exe файла, т.к. самого exe файла не будет. Но если у разработчика небольшой опыт в C++, то разобраться с ошибками от линкера ему будет непросто.

Поэтому лично я стараюсь держать опережающие объявления под контролем в специально огороженных загончиках. Либо кучу разных объявлений в одном файле (например), либо же только то, что относится к какому-то одному типу (например). Смысл есть и в том, и в другом вариантах.

Но вот когда опережающие объявления без контроля разбросаны по всему проекту, вот это меня сильно напрягает.

Комментариев нет: