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