Определение статических целочисленных членов const в определении класса


Я понимаю, что 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 89

7 ответов:

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

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

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

см. ответ Чу для возможного обходного пути.

пример Бьярне Страуструпа в его 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 вызывает адреса как-то за кулисами.

другой способ сделать это, для целочисленных типов в любом случае, чтобы определить константы как перечисления в классе:

class test
{
public:
    enum { N = 10 };
};

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

class classname
{
    public:
       static int const N;
}

in the .H-файл, то вы должны иметь:

int const classname::N = 10;

in the .файл cpp.

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

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

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

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

нет, 3.1 §2 говорит:

объявление является определением, если он объявляет функцию без указания тела функции (8.4), он содержит описатель extern (7.1.1) или спецификацию связи (7.5) и ни инициализатор, ни тело функции,он объявляет статический член данных в определении класса (9.4), это имя класса декларация (9.1), это непрозрачная декларация перечисления (7.2), или это декларация typedef (7.1.3), декларация использования (7.3.3) или директива использования (7.3.4).

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

static constexpr int N = 10;

это не требует от вас определения константы в a .файл cpp.