Подтвердить что ты не робот

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

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

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

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

const std::string Ten = "10";

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

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 не допускаются стандартом.

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

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

const std::string Ten = "10";

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

extern const std::string Ten;

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

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

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

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

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

Ответ 3

Версия 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. Это может показаться немного громоздким для одной переменной, но для более крупного проекта у вас, вероятно, будет хороший заголовок, который будет использоваться много, что вы можете вставить в него.

Ответ 4

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

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

Ответ 5

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

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

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