нецелые константы - PullRequest
       13

нецелые константы

16 голосов
/ 28 января 2010

Мне нужен заголовочный файл с нецелой константой, например, класс. Обратите внимание, что константа не должна быть константой времени компиляции.

static const std::string Ten = "10";

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

const std::string Ten = "10";

Это скомпилируется, но не удастся с ошибкой компоновщика для множества определенных Ten.

constexpr std::string Ten = "10"s;

Это будет работать, но только если конструктор строк тоже будет constexpr. Будет, но я не могу рассчитывать на каждую нецелую константу, чтобы иметь конструктор constexpr ... или я могу?

extern const std::string Ten = "10";

Кажется, что это работает, но я боюсь, что получу ошибку компоновщика, если перестану дышать.

inline const std::string Ten( ) { return "10"; }

Здесь есть все, что я хочу, кроме чистого синтаксиса. Плюс теперь я должен ссылаться на константу как на вызов функции, Ten().

inline const std::string = "10";

Кажется, это идеальное решение. Конечно, inline переменные не разрешены стандартом.

  • Есть ли в стандарте c ++ что-то, что внешняя версия должна работать, или мне просто повезло, что она работает с GCC?
  • Есть ли веская причина не разрешать встроенные переменные?
  • Есть ли лучший способ с c ++ 03 или будет лучший путь с c ++ 0x?

Ответы [ 5 ]

27 голосов
/ 28 января 2010

Кажется, вы их перепутали.

Вы правы насчет

static const std::string Ten = "10"; 

версия. Он будет «работать», но он создаст отдельный объект в каждой единице перевода.

Версия без static будет иметь тот же эффект. Он не будет вызывать ошибки компоновщика, но будет определять отдельный объект в каждой единице перевода. В языке C ++ const объекты имеют внутреннюю связь по умолчанию, что означает, что

const std::string Ten = "10"; // `static` is optional

в точности соответствует предыдущей версии с static.

Версия с extern и инициализатором

extern const std::string Ten = "10"; // it's a definition!

создаст определение объекта с внешней связью (это определение из-за присутствия инициализатора). Эта версия приведет к ошибкам компоновщика, так как вы получите несколько определений объекта с внешней связью - нарушение ODR.

Вот как вы можете это сделать:

Чтобы достичь того, чего вы пытаетесь достичь, вы должны объявить свою константу в заголовочном файле

extern const std::string Ten; // non-defining declaration

и затем определить его (с инициализатором) в одном и только одном из файлов реализации

extern const std::string Ten = "10"; // definition, `extern` optional

(Если константа предварительно объявлена ​​как extern, то extern в определении является необязательной. Даже без явного extern она будет определять объект const с внешней связью.)

10 голосов
/ 28 января 2010

Я не знаю, есть ли лучший способ в C ++, но лучший способ в C (который также будет работать в C ++) - один из тех, что вы перечислили.

Имеет отдельный блок компиляции (например, ten.cpp), содержащий только данные:

const std::string Ten = "10";

и заголовочный файл (например, ten.h), объявляющий его, чтобы его можно было использовать в другом месте:

extern const std::string Ten;

Тогда вам просто нужно убедиться, что любой модуль компиляции, который хочет его использовать, включает файл заголовка (например, ten.h) и любой исполняемый файл, который хочет использовать его, связывается с отдельным модулем компиляции (например, ten.o) .

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

я не знаю почему вы заявляете:

но я боюсь, что получу ошибку компоновщика, если неправильно на ней дышу

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

6 голосов
/ 28 января 2010

Версия extern близка к тому, что вы хотите. Здесь:

// in the file tenconstant.cpp
const std::string Ten = "10";

// in the file tenconstant.h
extern const std::string Ten;

// in your file
#include "tenconstant.h"

// do stuff with Ten

Вам нужно, чтобы он был определен один раз для компоновщика, что является целью myconstants.cpp, но объявляется везде, где вы его используете, что является целью myconstants.h. Это может показаться немного громоздким для одной переменной, но для более крупного проекта у вас, вероятно, будет хороший заголовок, который часто используется, и вы можете его вставить.

2 голосов
/ 28 января 2010

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

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

1 голос
/ 28 января 2010

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

inline const std::string &get_ten() {
    static const std::string ten = "10";
    return ten;
}

Будет только один string, инициализированный один раз, и вам ничего не нужно, кроме файла заголовка.

...