Определение статических константных элементов в определении класса
Мое понимание состоит в том, что С++ позволяет определять константные члены-члены внутри класса, если он является целым типом.
Почему тогда следующий код дает мне ошибку компоновщика?
#include <algorithm>
#include <iostream>
class test
{
public:
static const int N = 10;
};
int main()
{
std::cout << test::N << "\n";
std::min(9, test::N);
}
Ошибка, которую я получаю:
test.cpp:(.text+0x130): undefined reference to `test::N'
collect2: ld returned 1 exit status
Интересно, что если я прокомментирую вызов std:: min, код компилируется и ссылки просто прекрасны (хотя test:: N также ссылается на предыдущую строку).
Любая идея относительно того, что происходит?
Мой компилятор - gcc 4.4 в Linux.
Ответы
Ответ 1
Мое понимание состоит в том, что С++ позволяет определять константные члены в классе, если он является целым типом.
Вы вроде как правильно. Вы можете инициализировать статические константные интегралы в объявлении класса, но это не определение.
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm
Интересно, что если я прокомментирую вызов std:: min, код компилируется и ссылки просто прекрасны (даже если в предыдущей строке также упоминается test:: N).
Любая идея относительно того, что происходит?
std:: min принимает свои параметры по ссылке const. Если бы они взяли их по стоимости, у вас не было бы этой проблемы, но поскольку вам нужна ссылка, вам также нужно определение.
Здесь глава/стих:
9.4.2/4 - Если член данных static
имеет тип const
integer или const
перечисления, его объявление в определении класса может указывать константу инициализатор, который должен быть интегральным постоянным выражением (5.19). В этом случае член может фигурировать в интегральных постоянных выражениях. Член все еще должен быть определен в области пространства имен, если он используется в программе, и определение области пространства имен не должно содержать initializer.
См. ответ Chu для возможного обхода.
Ответ 2
Пример Bjarne Stroustrup в его часто задаваемых вопросах С++ предполагает, что вы правы и вам нужно только определение, если вы берете адрес.
class AE {
// ...
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
int f()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
// ...
}
Он говорит "Вы можете взять адрес статического члена, если (и только если) он имеет определение вне класса" . Это говорит о том, что это будет работать иначе. Возможно, ваша функция min вызывает адреса как-то за кулисами.
Ответ 3
Другим способом сделать это для целых типов в любом случае является определение констант как перечислений в классе:
class test
{
public:
enum { N = 10 };
};
Ответ 4
Не только int. Но вы не можете определить значение в объявлении класса. Если у вас есть:
class classname
{
public:
static int const N;
}
в файле .h, тогда вы должны:
int const classname::N = 10;
в файле .cpp.
Ответ 5
Вот еще один способ решения проблемы:
std::min(9, int(test::N));
(Я думаю, что Crazy Eddie отвечает правильно описывает, почему проблема существует.)
Ответ 6
Начиная с С++ 11 вы можете использовать:
static constexpr int N = 10;
Это теоретически все еще требует, чтобы вы определили константу в файле .cpp, но пока вы не берете адрес N
очень маловероятно, что любая реализация компилятора выдаст ошибку;).
Ответ 7
С++ позволяет статическим членам константы быть определенными внутри класса
Нет, 3.1 §2 гласит:
Объявление - это определение, если не объявляет функцию без указания тела функции (8.4), содержит спецификатор extern (7.1.1) или спецификацию привязки (7.5), и ни инициализатор или функция, объявляет статический член данных в определении класса (9.4), это декларация имени класса (9.1), это непрозрачная-enum-декларация (7.2), или она представляет собой объявление typedef (7.1.3), использование-декларация (7.3.3) или директиву (7.3.4).