Глобальные константы в C ++ 11
Каковы наилучшие способы объявления и определения глобальных констант в C ++? Меня больше всего интересует стандарт C ++ 11, так как он многое исправляет в этом отношении.
[РЕДАКТИРОВАТЬ (уточнение)]: в этом вопросе «глобальная постоянная» обозначает постоянную переменную или функцию, которая известна во время компиляции влюбой объем. Глобальная константа должна быть доступна из нескольких единиц перевода. Это не обязательно константа в стиле constexpr - может быть что-то вроде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
являются абсолютно безопасными, даже поточно-ориентированными с C ++ 11.b
не кажется хорошим выбором, еслиT
имеет очень дешевый конструктор, который редко встречается у конструкторов non-constexpr. Я могу назвать одно преимуществоc
надd
- нет вызова функции (производительность во время выполнения); одно преимуществоd
надc
- меньше перекомпиляции при изменении значения константы (эти преимущества также относятся кC
а такжеD
). Я уверен, что я пропустил много рассуждений здесь. Пожалуйста, укажите другие соображения в ответах.
Если вы хотите изменить / протестировать приведенный выше код, вы можете использовать мои тестовые файлы (просто header.hpp, source.cpp с компилируемыми версиями фрагментов кода выше и main.cpp, который печатает константы из header.hpp):https://docs.google.com/uc?export=download&id=0B0F-aqLyFk_PVUtSRnZWWnd4Tjg