Позвольте мне начать с заявления о моем намерении. В старинные (С++) дни у нас был бы код вроде:
class C
{
public:
enum {SOME_VALUE=27};
};
Тогда мы могли бы использовать SOME_VALUE
во всем нашем коде как постоянную времени компиляции, и везде, где компилятор увидит C::SOME_VALUE
, он просто вставляет литерал 27.
Теперь, кажется, более приемлемо изменить этот код на что-то вроде:
class C
{
public:
static constexpr int SOME_VALUE=27;
};
Это выглядит намного чище, дает SOME_VALUE
четко определенный тип и, по-видимому, является предпочтительным подходом с С++ 11. Проблема (непредвиденная, по крайней мере, для меня) заключается в том, что это также вызывает сценарии, в которых SOME_VALUE
необходимо сделать внешним. То есть в каком-то файле cpp нам нужно добавить:
constexpr int C::SOME_VALUE; // Now C::SOME_VALUE has external linkage
Случаи, которые приводят к этому, возникают, когда используются ссылки на ссылки SOME_VALUE
, что довольно часто встречается в коде стандартной библиотеки С++ (см. пример внизу этого вопроса). Кстати, я использую gcc 4.7.2 как свой компилятор.
Из-за этой дилеммы я вынужден вернуться к определению SOME_VALUE
как перечисления (т.е. старой школы), чтобы избежать необходимости добавлять определение к файлу cpp для некоторых, но не для всех моих статических переменные-члены constexpr. Разве нет способа сообщить компилятору, что constexpr int SOME_VALUE=27
означает, что SOME_VALUE
следует рассматривать только как константу времени компиляции и никогда не объект с внешней связью? Если вы видите ссылку на константу, используемую с ней, создайте временную. Если вы видите его адрес, сгенерируйте ошибку времени компиляции, если это необходимо, потому что это константа времени компиляции и ничего больше.
Вот какой-то, казалось бы, доброкачественный образец кода, который заставляет нас добавить определение для SOME_VALUE
в файл cpp (еще раз, проверено с помощью gcc 4.7.2):
#include <vector>
class C
{
public:
static constexpr int SOME_VALUE=5;
};
int main()
{
std::vector<int> iv;
iv.push_back(C::SOME_VALUE); // Will cause an undefined reference error
// at link time, because the compiler isn't smart
// enough to treat C::SOME_VALUE as the literal 5
// even though it obvious at compile time
}
Добавление следующей строки в код в области файла приведет к ошибке:
constexpr int C::SOME_VALUE;