Глобальные константы в 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

Ответы на вопрос(3)

Ваш ответ на вопрос