Каковы наилучшие способы объявления и определения глобальных констант в С++? Меня больше всего интересует стандарт С++ 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. Я могу назвать одно преимуществоCoverD- отсутствие вызова функции (время выполнения); одно преимуществоDoverC- меньше перекомпиляции при изменении постоянного значения (эти преимущества также применимы кCиD). Я уверен, что я пропустил много рассуждений. Предоставьте другие соображения в ответах.
Если вы хотите изменить/протестировать вышеуказанный код, вы можете использовать мои тестовые файлы (только header.hpp, source.cpp с компилируемыми версиями вышеописанных фрагментов кода и main.cpp, который печатает константы из header.hpp): https://docs.google.com/uc?export=download&id=0B0F-aqLyFk_PVUtSRnZWWnd4Tjg