简体   繁体   English

未定义引用 static class 成员

[英]Undefined reference to static class member

Can anyone explain why following code won't compile?谁能解释为什么以下代码无法编译? At least on g++ 4.2.4.至少在 g++ 4.2.4。

And more interesting, why it will compile when I cast MEMBER to int?更有趣的是,为什么当我将 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;
}

You need to actually define the static member somewhere (after the class definition).您需要在某处实际定义静态成员(在类定义之后)。 Try this:试试这个:

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

const int Foo::MEMBER;

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

That should get rid of the undefined reference.那应该摆脱未定义的引用。

The problem comes because of an interesting clash of new C++ features and what you're trying to do.问题的出现是因为新的 C++ 功能和您正在尝试做的事情之间发生了有趣的冲突。 First, let's take a look at the push_back signature:首先,我们看一下push_back签名:

void push_back(const T&)

It's expecting a reference to an object of type T .它期望引用T类型的对象。 Under the old system of initialization, such a member exists.在旧的初始化系统下,存在这样的成员。 For example, the following code compiles just fine:例如,以下代码编译得很好:

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

This is because there is an actual object somewhere that has that value stored in it.这是因为某处存在一个实际对象,其中存储了该值。 If, however, you switch to the new method of specifying static const members, like you have above, Foo::MEMBER is no longer an object.但是,如果您切换到指定静态 const 成员的新方法,就像上面那样, Foo::MEMBER不再是一个对象。 It is a constant, somewhat akin to:它是一个常数,有点类似于:

#define MEMBER 1

But without the headaches of a preprocessor macro (and with type safety).但是没有预处理器宏的麻烦(并且具有类型安全性)。 That means that the vector, which is expecting a reference, can't get one.这意味着需要参考的向量无法获得参考。

The C++ standard requires a definition for your static const member if the definition is somehow needed.如果以某种方式需要定义,C++ 标准需要为您的静态 const 成员定义。

The definition is required, for example if it's address is used.定义是必需的,例如,如果使用它的地址。 push_back takes its parameter by const reference, and so strictly the compiler needs the address of your member and you need to define it in the namespace. push_back通过 const 引用获取其参数,因此严格来说编译器需要您的成员的地址,您需要在命名空间中定义它。

When you explicitly cast the constant, you're creating a temporary and it's this temporary which is bound to the reference (under special rules in the standard).当您显式转换常量时,您正在创建一个临时对象,并且它是绑定到引用的临时对象(根据标准中的特殊规则)。

This is a really interesting case, and I actually think it's worth raising an issue so that the std be changed to have the same behaviour for your constant member!这是一个非常有趣的案例,我实际上认为值得提出一个问题,以便将 std 更改为对您的常量成员具有相同的行为!

Although, in a weird kind of way this could be seen as a legitimate use of the unary '+' operator.虽然,以一种奇怪的方式,这可以被视为对一元“+”运算符的合法使用。 Basically the result of the unary + is an rvalue and so the rules for binding of rvalues to const references apply and we don't use the address of our static const member:基本上unary +的结果是一个右值,因此将右值绑定到 const 引用的规则适用,我们不使用静态 const 成员的地址:

v.push_back( +Foo::MEMBER );

Aaa.h啊啊啊

class Aaa {

protected:

    static Aaa *defaultAaa;

};

Aaa.cpp aaa.cpp

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

static Aaa *Aaa::defaultAaa;

In C++17, there is an easier solution using inline variables:在 C++17 中,使用inline变量有一个更简单的解决方案:

struct Foo{
    inline static int member;
};

This is a definition of member , not just its declaration.这是member的定义,而不仅仅是它的声明。 Similar to inline functions, multiple identical definitions in different translation units do not violate ODR.与内联函数类似,不同翻译单元中的多个相同定义不会违反 ODR。 There is no longer any need to pick a favourite .cpp file for the definition.不再需要为定义选择最喜欢的 .cpp 文件。

Just some additional info:只是一些额外的信息:

C++ allows to "define" const static types of integral and enumeration types as class members. C++ 允许将整型和枚举类型的 const 静态类型“定义”为类成员。 But this is actually not a definition, just an "initializiation-marker"但这实际上不是一个定义,只是一个“初始化标记”

You should still write a definition of your member outside of the class.您仍然应该在类之外编写您的成员的定义。

9.4.2/4 - If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). 9.4.2/4 - 如果一个静态数据成员是 const 整数或 const 枚举类型,它在类定义中的声明可以指定一个常量初始化器,它应该是一个整数常量表达式 (5.19)。 In that case, the member can appear in integral constant expressions.在这种情况下,成员可以出现在整型常量表达式中。 The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.如果在程序中使用该成员,则该成员仍应在名称空间范围内定义,并且名称空间范围定义不应包含初始值设定项。

No idea why the cast works, but Foo::MEMBER isn't allocated until the first time Foo is loaded, and since you're never loading it, it's never allocated.不知道为什么演员表起作用,但是直到第一次加载 Foo 时才分配 Foo::MEMBER ,并且由于您从不加载它,因此永远不会分配它。 If you had a reference to a Foo somewhere, it would probably work.如果您在某处引用了 Foo,它可能会起作用。

With C++11, the above would be possible for basic types as使用 C++11,以上对于基本类型是可能的

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

The constexpr part creates a static expression as opposed to a static variable - and that behaves just like an extremely simple inline method definition. constexpr部分创建一个静态表达式,而不是一个静态变量——它的行为就像一个非常简单的内联方法定义。 The approach proved a bit wobbly with C-string constexprs inside template classes, though.不过,在模板类中使用 C 字符串 constexprs 时,该方法被证明有点不稳定。

Regarding the second question: push_ref takes reference as a parameter, and you cannot have a reference to static const memeber of a class/struct.关于第二个问题: push_ref 将引用作为参数,并且您不能引用类/结构的静态 const 成员。 Once you call static_cast, a temporary variable is created.调用 static_cast 后,将创建一个临时变量。 And a reference to this object can be passed, everything works just fine.并且可以传递对该对象的引用,一切正常。

Or at least my colleague who resolved this said so.或者至少我解决这个问题的同事是这么说的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM