Ответ 1
Необходимое определение
Код, который вы предоставили, является нестандартным. Хотя вы можете предоставить инициализаторы для константных статических членов int непосредственно в классе, вам все равно необходимо предоставить отдельные определения. Это странно, что-то неожиданное, но вы должны написать его вот так:
#include <algorithm>
struct Foo
{
static const int A = 1;
static const int B = 2;
};
const int Foo::A;
const int Foo::B;
int main()
{
return std::min(Foo::A, Foo::B);
}
Цитата из стандарта можно найти в аналогичном вопросе в константных и статических спецификациях в С++
Почему иногда код "работает" без определения?
Что касается того, почему вы часто можете обойтись даже без предоставления определения: если вы используете эти члены только в постоянных выражениях, компилятор будет всегда решать их напрямую и не будет доступа для разрешения компоновщика. Только когда вы используете его каким-то образом, который не может быть обработан компилятором напрямую, и только в этом случае компоновщик обнаружит символ undefined. Я думаю, это, вероятно, ошибка в компиляторе Visual Studio, но с учетом характера ошибки я сомневаюсь, что она будет когда-либо исправлена.
Почему ваш источник попадает в категорию "компоновщик" - это то, чего я не вижу, нужно было бы разбирать std:: min, чтобы понять это. Примечание. Когда я пробовал его онлайн с GCC, он работал, ошибка не обнаруживалась.
Альтернатива: используйте enum
Другой альтернативой является использование перечисления. Эта версия также может пригодиться, когда вы нажмете на старый компилятор, который не поддерживает инициализаторы static in int int inline (например, Visual Studio 6). Обратите внимание, однако, что при std:: min вы сталкиваетесь с другими проблемами с перечислениями, и вам нужно использовать явное инстанцирование или кастинг или иметь как A, так и B в одном названии enum, как в ответ от Nawaz:
struct Foo
{
enum {A = 1};
enum {B = 2};
};
int main()
{
return std::min<int>(Foo::A, Foo::B);
}
Стандарты
Примечание: даже Часто задаваемые вопросы по Stroustrup С++ ошибочно и не требует определения строго так, как это делает стандарт:
Вы можете взять адрес статического члена, если (и только если) он имеет определение вне класса
Определение , требуемое стандартом в 9.4.2:
С++ 03:
Член все еще должен быть определен в области пространства имен, если он используется в программе, и определение области пространства имен не должно содержать инициализатор
С++ 11 формулировка 9.4.2 немного отличается:
3 Элемент все еще должен быть определен в области пространства имен, если он используется в odr (3.2) в программе
3.2 говорится следующее о использовании odr:
3 Переменная x, имя которой отображается как потенциально вычисленное выражение ex, используется odr, если только x не является объектом, удовлетворяющим требованиям к появлению в постоянном выражении (5.19), а ex является элементом набора потенциальных результатов выражения e, где либо преобразование lvalue-to-rvalue (4.1) применяется к e, либо e является выражением отбрасываемого значения (раздел 5).
4 Каждая программа должна содержать ровно одно определение каждой не-встроенной функции или переменной, которая используется в этой программе odr; не требуется диагностика.
Я должен признать, что не знаю, каковы точные последствия формулировки С++ 11, поскольку я не понимаю правила использования odr.