关于c ++:对静态类成员的未定义引用

Undefined reference to static class member

有人能解释一下为什么下面的代码不能编译吗?至少在G++4.2.4上。

更有趣的是,当我将成员强制转换为int时,它为什么要编译?

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

class Foo {  
public:  
    static const int MEMBER = 1;  
};

int main(){  
    vector<int> v;  
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}


您需要在某个地方(在类定义之后)实际定义静态成员。试试这个:

1
2
3
4
5
class Foo { /* ... */ };

const int Foo::MEMBER;

int main() { /* ... */ }

这应该消除未定义的引用。


问题是因为新的C++特性和你想做的事情发生了一个有趣的冲突。首先,让我们看一下push_back签名:

1
void push_back(const T&)

它期望引用T类型的对象。在旧的初始化系统中,存在这样的成员。例如,以下代码编译得很好:

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

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1;

int main(){
    std::vector<int> v;
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

这是因为有一个实际的对象在某个地方存储了该值。但是,如果您像上面那样切换到指定静态常量成员的新方法,那么Foo::MEMBER不再是对象。它是一个常量,有点类似于:

1
#define MEMBER 1

但是没有预处理器宏的麻烦(也没有类型安全)。这意味着期望引用的向量不能得到一个。


如果需要某种定义,C++标准需要为静态const成员定义一个定义。

例如,如果使用了地址,则需要定义。push_back通过常量引用获取其参数,因此严格来说,编译器需要成员的地址,而您需要在命名空间中定义它。

当您显式地强制转换常量时,您将创建一个临时的,并且它是绑定到引用的临时的(根据标准中的特殊规则)。

这是一个非常有趣的案例,我实际上认为值得提出一个问题,这样性病就可以被改变为对你的常任会员同样的行为!

尽管如此,以一种奇怪的方式,这可以被视为一元"+"运算符的合法使用。基本上,unary +的结果是一个右值,因此将右值绑定到常量引用的规则适用,我们不使用静态常量成员的地址:

1
v.push_back( +Foo::MEMBER );


AA. H

1
2
3
4
5
6
7
class Aaa {

protected:

    static Aaa *defaultAaa;

};

美国石油协会

1
2
3
// You must define an actual variable in your program for the static members of the classes

static Aaa *Aaa::defaultAaa;


不知道为什么强制转换有效,但是foo::member直到第一次加载foo时才被分配,而且由于您从未加载它,所以它从未被分配。如果你在某个地方提到了foo,它可能会起作用。


用C++ 11,上面的基本类型是可能的。

1
2
3
4
class Foo {
public:  
  static constexpr int MEMBER = 1;  
};

constexpr部分创建了一个静态表达式,而不是一个静态变量,它的行为就像一个非常简单的内联方法定义。不过,在模板类中使用C字符串constexpr证明了这种方法有点不稳定。


关于第二个问题:push_-ref将引用作为参数,并且不能引用类/结构的静态const memeber。一旦调用static_cast,就会创建一个临时变量。可以传递对这个对象的引用,一切都正常。

或者至少我解决这个问题的同事这么说。