Помещение константы в заголовок - программирование
Подтвердить что ты не робот

Помещение константы в заголовок

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

Я могу это сделать:

const int maxtt=888888;

То же самое, что и:

static const int maxtt=888888;

И если этот заголовок будет общим, он будет работать, но каждый TU получит свою собственную копию maxtt. Я также мог бы сделать это, чтобы предотвратить это:

extern const int maxtt;

Но тогда я не могу определить maxtt здесь; это должно быть сделано в CPP, чтобы избежать ошибки компоновщика.

Правильно ли я понимаю?

4b9b3361

Ответ 1

Поскольку переменная постоянна, тот факт, что каждый TU получает свою собственную копию, обычно не имеет значения.

В С++ константы в области пространства имен неявно static по этой причине. Часто это позволяет получить более качественный код, чем если бы у вас был только один экземпляр с внешней связью, поскольку (если переменная на самом деле является постоянным выражением), константу можно часто складывать прямо на сайт использования и не нужно вообще хранить.

Поэтому, если вам действительно не нужно принимать адрес константы или что-то в этом роде, вы должны придерживаться статической версии. (И, как вы уже заметили, вы можете принудительно установить внешнюю связь, добавив extern.) Еще одна причина может заключаться в том, что вы инициализируетесь динамически и хотите только один вызов инициализатора:

// header:
extern int const n;

// one implementation:
int const n = init_function_with_side_effects();

Статическая конструкция (int const n = init(); в заголовке) заставит функцию вызываться один раз в каждом TU.

Ответ 2

Вы пишете,

"Из всего, что я читаю и тестирую, нет способа (без макроса препроцессора) определить константу в общем заголовке и убедиться, что каждый TU не создает собственное хранилище для этой константы".

К счастью, s неверно.

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

Однако экономия места для целочисленного значения является довольно бессмысленной задачей, так как она настолько мала.

Итак, пусть & rsquo; s рассмотрит biggie thingy,

struct BiggyThingy
{
    unsigned char zeroes[1000000];
    BiggyThingy(): zeroes() {}
};

Теперь как мы можем объявить константу BiggyThingy в файле заголовка и обеспечить единую общую для всей программы?

Использование встроенной функции.

Ну, самое простое:

inline BiggyThingy const& getBiggyThingy()
{
    static BiggyThingy const theThingy;
    return theThingy;
}

static BiggyThingy const& biggyThingy = getBiggyThingy();

Если вы don & rsquo; t хотите, чтобы ссылка занимала пробел (например, указатель) в каждой единицы перевода, тогда просто используйте эту функцию без упрощающей запись ссылки.

Использование тэга константы шаблона.

Вот еще один способ предоставить константу, используя вместо этого специальное правило для шаблонов:

template< class Dummy >
class BiggyThingyConstant_
{
public:
    static BiggyThingy const value;
};

template< class Dummy >
BiggyThingy const BiggyThingyConstant_<Dummy>::value;

typedef BiggyThingyConstant_<void> BiggyThingyConstant;

к которому можно получить доступ, например

foo( BiggyThingyConstant::value )

Или, если вам нужна более удобная нотация, вы можете добавить ссылку на единицу перевода, как для решения встроенной функции.

Отказ от ответственности:

Код не тронут компилятором.

Но вы, кажется, получаете идеи.; -)

Ответ 3

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

static int maxtt = 888888;
int * pmaxtt = &maxtt; //address of constant requested.

Это также может работать и позволяет избежать проблемы с компоновщиком (хотя он будет хранить maxtt в каждом TU, если адрес запрашивается):

constexpr int maxtt = 888888;

Избегайте создания extern, поскольку его нельзя оптимизировать.

Ответ 4

Если вы так обеспокоены хранением, используйте перечисление:

enum { maxtt = 888888 };

Перечислители являются скалярными значениями и, следовательно, не требуют хранения. Нельзя сказать &maxtt.

Ответ 5

В самом деле, ваше понимание семантики верное.

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

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

У вас может быть аналогичная проблема, если вы используете перечисление; ваш const int имеет хранилище, и вы можете взять адрес этого хранилища. Вы не можете взять адрес enumerand, пока не сохраните его где-нибудь.