Как компилятор узнает, как другие файлы .cpp используют статический член const?

Может кто-нибудь объяснить мне этот пример из наиболее авторитетных ISO C ++ FAQ? Код выглядит так:

// Fred.h
class Fred {
public:
static const int maximum = 42;
// ...
};

// Fred.cpp
#include "Fred.h"const int Fred::maximum;
// ...

И утверждение, которое я не могу получить:

Если вы когда-нибудь возьмете адрес Fred :: Maximum, например, передадите его по ссылке или явно &Fred :: Maximum, компилятор позаботится о том, чтобы у него был уникальный адрес. Если нет, Fred :: Maximum не займет даже места в области статических данных вашего процесса.

Компилятор обрабатывает файлы .cpp отдельно и не знает, что делают другие файлы с данными, определенными в обрабатываемых в данный момент. Итак, как компилятор может решить, должен ли он выделять уникальный адрес или нет?

Оригинальный предмет здесь: https://isocpp.org/wiki/faq/ctors#static-const-with-initializers

1

Решение

Запись FAQ говорит, что const int Fred::maximum; должен быть определен ровно в одной единице компиляции. Однако это верно только в том случае, если переменная УСО используемый программой (например, если к ней привязана ссылка).

Если переменная не УСО используемый тогда определение может быть опущено.

Однако, если переменная на самом деле УСО используемый но не имеет определения, то это неопределенное поведение без диагностической необходимости. Как правило, если адрес переменной требуется, но определение опущено, компоновщик хорошего качества пропустит ошибку «неопределенная ссылка».

Но вы не всегда хотите полагаться на определенные проявления неопределенного поведения. Поэтому рекомендуется всегда включать определение const int Fred::maximum;,


Процитированный абзац в вашем вопросе предназначен для решения проблемы потенциального программиста: «Ну, я не могу сохранить 4 байта в своей области статических данных, опуская определение в некоторых случаях?»

Это говорит о том, что компилятор / компоновщик мог выполнить анализ всей программы и принять собственное решение по оптимизации, чтобы опустить определение, если оно определило, что определение не использовалось.

Хотя линия const int Fred::maximum; определяется как выделение памяти для intэто разрешенная оптимизация, потому что соответствующая программа не может измерить, была ли память фактически выделена для int который не УСО используемый.

Автор этой записи FAQ явно ожидает, что компилятор / компоновщик действительно сделает это.


Формулировка в Стандарте о УСО использование предназначен для поддержки следующей модели компиляции / компоновки:

  • Если некоторый код требует, чтобы переменная имела адрес, объектный файл будет содержать ссылку на переменную.
  • Оптимизация может удалить некоторые пути кода, которые никогда не вызываются и т. Д.
  • Во время соединения, когда эти ссылки разрешены, эта ссылка будет привязана к определению переменной.

Для этого не требуется, чтобы компилятор выдавал сообщение об ошибке «неопределенная ссылка», потому что это усложняло бы оптимизацию компиляторов. Может случиться, что на другом этапе оптимизации будет полностью удалена часть объектного файла, содержащая ссылку. Например, если оказалось УСО использование только когда-либо происходило в функции, которая никогда не вызывалась.

0

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

Компилятор ничего не решает. Для переводческих единиц, где static член класса не определен, объектный модуль, созданный компилятором, содержит неразрешенную ссылку на символ.

Когда все объектные модули связаны друг с другом, компоновщик отвечает за завершение работы и разрешение всех неразрешенных ссылок от единиц перевода, ссылающихся на статический символ, на единичную единицу перевода, для которой определен символ.

1