Определение статических целочисленных членов const в определении класса - PullRequest
96 голосов
/ 12 июня 2010

Насколько я понимаю, C ++ позволяет определять статические члены const внутри класса, если это целочисленный тип.

Почему следующий код дает мне ошибку компоновщика?

#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.

Ответы [ 7 ]

64 голосов
/ 12 июня 2010

Насколько я понимаю, C ++ позволяет определять статические члены const внутри класса, если это целочисленный тип.

Вы вроде как правы. Вам разрешено инициализировать статические интегралы const в объявлении класса, но это не определение.

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 принимает свои параметры по константной ссылке. Если бы он брал их по значению, у вас не было бы этой проблемы, но, поскольку вам нужна ссылка, вам также нужно определение.

Вот глава / стих:

9.4.2 / 4 - Если элемент данных static имеет const целочисленный или const тип перечисления, его объявление в определении класса может указывать constant-initializer , которое должно быть интегральным постоянным выражением (5.19). В этом случае член может появляться в виде целочисленных константных выражений. Член по-прежнему должен быть определен в области имен, если он используется в программе, и определение области имен не должно содержать инициализатор .

См. Ответ Чу о возможном обходном пути.

45 голосов
/ 12 июня 2010

Пример Бьярна Страуструпа в его C ++ FAQ предполагает, что вы правы, и вам нужно только определение, если вы берете адрес.

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 вызывает адреса как-то негласно.

23 голосов
/ 12 июня 2010

Еще один способ сделать это для целочисленных типов - определить константы как перечисления в классе:

class test
{
public:
    enum { N = 10 };
};
10 голосов
/ 12 июня 2010

Не только int.Но вы не можете определить значение в объявлении класса.Если в файле .h указано:

class classname
{
    public:
       static int const N;
}

, то в файле .cpp должно быть:

int const classname::N = 10;

.

9 голосов
/ 28 мая 2011

Вот еще один способ обойти проблему:

std::min(9, int(test::N));

(Я думаю, что ответ Безумного Эдди правильно описывает причину проблемы)

4 голосов
/ 12 сентября 2017

Начиная с C ++ 11 вы можете использовать:

static constexpr int N = 10;

Это теоретически все еще требует, чтобы вы определяли константу в файле .cpp, но до тех пор, пока вы неЕсли взять адрес N, очень маловероятно, что любая реализация компилятора выдаст ошибку;).

3 голосов
/ 12 июня 2010

C ++ позволяет определять статические члены const внутри класса

Нет, 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).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...