关于c ++:在类定义中定义静态const整数成员

Defining static const integer members in class definition

我的理解是C++允许静态const成员在一个类中定义,只要它是整数类型。

那么,为什么下面的代码会给我一个链接器错误?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include
#include <iostream>

class test
{
public:
    static const int N = 10;
};

int main()
{
    std::cout << test::N <<"
"
;
    std::min(9, test::N);
}

我得到的错误是:

1
2
test.cpp:(.text+0x130): undefined reference to `test::N'
collect2: ld returned 1 exit status

有趣的是,如果我注释掉对std::min的调用,代码编译和链接就很好了(即使在前一行中也引用了test::n)。

知道发生了什么事吗?

我的编译器是Linux上的GCC4.4。


我的理解是C++允许静态const成员在一个类中定义,只要它是整数类型。

你是对的。允许在类声明中初始化静态常量积分,但这不是定义。

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?主题=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm

有趣的是,如果我注释掉对std::min的调用,代码编译和链接就很好了(即使在前一行中也引用了test::n)。

知道发生了什么事吗?

std::min通过常量引用获取其参数。如果按价值计算,你就不会有这个问题,但是因为你需要一个参考,你也需要一个定义。

以下是章节:

9.4.2/4-如果static数据成员是const整数或const枚举类型,则其在类定义中的声明可以指定一个常量初始值设定项,该常量初始值设定项应为整数常量表达式(5.19)。在这种情况下,成员可以出现在整型常量表达式中。如果在程序中使用成员,则该成员仍应在命名空间范围中定义,并且命名空间范围定义不应包含初始值设定项。

关于可能的解决办法,请参阅朱棣文的答案。


Bjarne Stroustrup在他的C++ FAQ中的例子表明你是正确的,只需要一个定义,如果你把地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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函数会在后台以某种方式调用地址。


对于整数类型,另一种方法是将常量定义为类中的枚举:

1
2
3
4
5
class test
{
public:
    enum { N = 10 };
};


不仅是in t的,而且不能在类声明中定义值。如果你有:

1
2
3
4
5
class classname
{
    public:
       static int const N;
}

在.h文件中,您必须具有:

1
int const classname::N = 10;

在.cpp文件中。


解决这个问题的另一种方法是:

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

(我认为疯狂埃迪的回答正确地描述了问题存在的原因。)


至于C++ 11,你可以使用:

static constexpr int N = 10;

理论上,这仍然要求您在.cpp文件中定义常量,但只要不使用EDOCX1的地址(4),任何编译器实现都不太可能产生错误;)。


C++ allows static const members to be defined inside a class

不,3.1第2节说:

A declaration is a definition unless it declares a function without specifying the function's body (8.4), it contains the extern specifier (7.1.1) or a linkage-specification (7.5) and neither an initializer nor a functionbody, it declares a static data member in a class definition (9.4), it is a class name declaration (9.1), it is an opaque-enum-declaration (7.2), or it is a typedef declaration (7.1.3), a using-declaration (7.3.3), or a using-directive (7.3.4).