Каковы наилучшие способы объявления и определения глобальных констант в С++? Меня больше всего интересует стандарт С++ 11, так как он многое исправляет в этом отношении.
[EDIT (уточнение)]: в этом вопросе "глобальная константа" обозначает постоянную переменную или функцию, которая известна во время компиляции в любой области. Глобальная константа должна быть доступна из нескольких единиц перевода. Это не обязательно константа constexpr-style - может быть что-то вроде const std::map<int, std::string> m = { { 1, "U" }, { 5, "V" } };
или const std::map<int, std::string> * mAddr() { return & m; }
. В этом вопросе я не затрагиваю предпочтительный стиль или имя хорошего стиля. Давайте оставим эти вопросы по другому вопросу. [END_EDIT]
Я хочу знать ответы для всех разных случаев, поэтому предположим, что T
является одним из следующих:
typedef int T; // 1
typedef long double T; // 2
typedef std::array<char, 1> T; // 3
typedef std::array<long, 1000> T; // 4
typedef std::string T; // 5
typedef QString T; // 6
class T {
// unspecified amount of code
}; // 7
// Something special
// not mentioned above? // 8
Я считаю, что нет большой семантики (я не обсуждаю здесь хороший стиль именования или рамки) разницу между тремя возможными областями:
// header.hpp
extern const T tv;
T tf(); // Global
namespace Nm {
extern const T tv;
T tf(); // Namespace
}
struct Cl {
static const T tv;
static T tf(); // Class
};
Но если выбрать лучший способ из альтернатив ниже, зависит от разницы между областями декларации объявления, пожалуйста, укажите его.
Рассмотрим также случай, когда вызов функции используется в постоянном определении, например. <some value>==f();
. Как вызов функции при постоянной инициализации влияет на выбор между альтернативами?
-
Рассмотрим сначала конструкцию
T
с конструкторомconstexpr
. Очевидными альтернативами являются:// header.hpp namespace Ns { constexpr T A = <some value>; constexpr T B() { return <some value>; } inline const T & C() { static constexpr T t = <some value>; return t; } const T & D(); } // source.cpp const T & Ns::D() { static constexpr T t = <some value>; return t; }
Я считаю, что
A
иB
наиболее подходят для небольшихT
(таких, что наличие нескольких экземпляров или их копирование во время выполнения не является проблемой), например.1-3
, иногда7
.C
иD
лучше, еслиT
велико, например.4
, иногда7
. -
T
без конструктораconstexpr
. Альтернативы:// header.hpp namespace Ns { extern const T a; inline T b() { return <some value>; } inline const T & c() { static const T t = <some value>; return t; } const T & d(); } // source.cpp extern const T Ns::a = <some value>; const T & Ns::d() { static const T t = <some value>; return t; }
Я обычно не использовал бы
A
из-за фиаско порядка статической инициализации. Насколько я знаю,B
,C
иD
являются абсолютно безопасными, даже потокобезопасными с С++ 11.B
, по-видимому, не является хорошим выбором, еслиT
не имеет очень дешевого конструктора, что необычно для конструкторов без constexpr. Я могу назвать одно преимуществоC
overD
- отсутствие вызова функции (время выполнения); одно преимуществоD
overC
- меньше перекомпиляции при изменении постоянного значения (эти преимущества также применимы кC
иD
). Я уверен, что я пропустил много рассуждений. Предоставьте другие соображения в ответах.
Если вы хотите изменить/протестировать вышеуказанный код, вы можете использовать мои тестовые файлы (только header.hpp, source.cpp с компилируемыми версиями вышеописанных фрагментов кода и main.cpp, который печатает константы из header.hpp): https://docs.google.com/uc?export=download&id=0B0F-aqLyFk_PVUtSRnZWWnd4Tjg