对静态类成员的未定义引用

时间:2008-11-07 17:39:56

标签: c++ g++

任何人都可以解释为什么以下代码无法编译?至少在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;
}

8 个答案:

答案 0 :(得分:189)

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

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

const int Foo::MEMBER;

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

那应该摆脱未定义的引用。

答案 1 :(得分:71)

问题来自于新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

但没有预处理器宏(并且具有类型安全性)的头痛。这意味着期望参考的向量不能得到一个。

答案 2 :(得分:58)

如果以某种方式需要定义,则C ++标准需要静态const成员的定义。

该定义是必需的,例如,如果使用了它的地址。 push_back通过const引用获取其参数,因此编译器需要您的成员的地址,您需要在命名空间中定义它。

当你明确地施放常量时,你正在创建一个临时的,这是临时的,它绑定到引用(在标准中的特殊规则下)。

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

虽然,以一种奇怪的方式,这可以被视为合法使用一元'+'运算符。基本上unary +的结果是一个rvalue,所以rvalues绑定到const引用的规则适用,我们不使用静态const成员的地址:

v.push_back( +Foo::MEMBER );

答案 3 :(得分:10)

<强> 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;

答案 4 :(得分:3)

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

struct Foo{
    inline static int member;
};

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

答案 5 :(得分:1)

不知道为什么演员会工作,但Foo :: MEMBER直到第一次加载Foo时才会被分配,而且由于你从未加载它,所以它永远不会被分配。如果你在某个地方引用了Foo,它可能会有效。

答案 6 :(得分:0)

关于第二个问题:push_ref将reference作为参数,并且您不能引用类/ struct的static const memeber。调用static_cast后,将创建一个临时变量。并且可以传递对该对象的引用,一切正常。

或至少我的同事解决了这个问题。

答案 7 :(得分:0)

使用C ++ 11,上面的基本类型可以作为

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

constexpr部分创建静态表达式而不是静态变量 - 其行为就像一个非常简单的内联方法定义。不过,这种方法在模板类中使用C-string constexprs时有点摇摆不定。