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

更有趣的是,为什么我将MEMBER转换为int时它会编译?

#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;
}

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

class Foo { /* ... */ };

const int Foo::MEMBER;

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

这样就可以去掉未定义的引用。


问题的出现是由于新的c++特性和您正在尝试做的事情之间的有趣冲突。首先,让我们看一下push_back签名:

void push_back(const T&)

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

#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;
}

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

#define MEMBER 1

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


不知道为什么转换可以工作,但是Foo::MEMBER直到Foo第一次加载时才被分配,因为你从来没有加载它,所以它从来没有被分配过。如果您在某个地方引用了Foo,那么它可能会工作。


如果在某种程度上需要静态const成员的定义,则c++标准要求该定义。

定义是必需的,例如如果使用了地址。Push_back通过const引用接受其形参,因此严格来说编译器需要你的成员的地址,你需要在命名空间中定义它。

当显式强制转换常量时,就创建了一个临时对象,而这个临时对象被绑定到引用(在标准中的特殊规则下)。

这是一个非常有趣的案例,我实际上认为值得提出一个问题,以便将std更改为对常量成员具有相同的行为!

虽然,以一种奇怪的方式,这可以被视为一元“+”操作符的合法使用。基本上,一元类型+的结果是一个右值,因此适用于将右值绑定到const引用的规则,我们不使用静态const成员的地址:

v.push_back( +Foo::MEMBER );

Aaa.h

class Aaa {

protected:

    static Aaa *defaultAaa;

};

Aaa.cpp

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

static Aaa *Aaa::defaultAaa;

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

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


在c++ 11中,上述基本类型可能是

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

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


在c++ 17中,使用内联变量有一个更简单的解决方案:

struct Foo{
    inline static int member;
};

这是一个成员的定义,而不仅仅是它的声明。与内联函数类似,不同翻译单元中的多个相同定义不会违反ODR。不再需要为定义选择最喜欢的.cpp文件。


只是一些额外的信息:

c++允许将整型的const静态类型和枚举类型“定义”为类成员。但这实际上不是定义,只是一个“初始化标记”

您仍然应该在类之外编写成员的定义。

9.4.2/4 -如果一个静态数据成员是const整型或const枚举类型,它在类定义中的声明可以指定一个常量初始化式,该常量初始化式必须是整型常量表达式(5.19)。在这种情况下,成员可以出现在积分常数表达式中。如果在程序中使用该成员,则该成员仍应定义在名称空间作用域中,且名称空间作用域定义不应包含初始化式。