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

发布于 2024-07-08 17:32:29 字数 382 浏览 9 评论 0 原文

谁能解释为什么以下代码无法编译? 至少在 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;
}

Can anyone explain why following code won't compile? At least on g++ 4.2.4.

And more interesting, why it will compile when I cast MEMBER to 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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

冷清清 2024-07-15 17:32:29

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

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

const int Foo::MEMBER;

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

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

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.

情话难免假 2024-07-15 17:32:29

问题的出现是由于新的 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

但没有预处理器宏的麻烦(并且具有类型安全性)。 这意味着需要引用的向量无法获得引用。

The problem comes because of an interesting clash of new C++ features and what you're trying to do. First, let's take a look at the push_back signature:

void push_back(const T&)

It's expecting a reference to an object of type 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. 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.

千と千尋 2024-07-15 17:32:29

如果出于某种原因需要定义,则 C++ 标准要求静态 const 成员的定义。

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

当您显式转换常量时,您正在创建一个临时变量,并且这个临时变量绑定到引用(根据标准中的特殊规则)。

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

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

v.push_back( +Foo::MEMBER );

The C++ standard requires a definition for your static const member if the definition is somehow needed.

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.

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!

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:

v.push_back( +Foo::MEMBER );
新一帅帅 2024-07-15 17:32:29

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;

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;
仅冇旳回忆 2024-07-15 17:32:29

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

struct Foo{
    inline static int member;
};

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

In C++17, there is an easier solution using inline variables:

struct Foo{
    inline static int member;
};

This is a definition of member, not just its declaration. Similar to inline functions, multiple identical definitions in different translation units do not violate ODR. There is no longer any need to pick a favourite .cpp file for the definition.

何时共饮酒 2024-07-15 17:32:29

只是一些附加信息:

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

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

如果非易失性非内联 const 静态数据成员是整型或枚举类型,则其在类定义中的声明可以指定brace-or-equal-initializer 其中每个初始化子句 这是一个赋值表达式< /em> 是一个常量表达式
如果该成员是 rel="nofollow noreferrer">odr-used 在程序中且命名空间范围定义不得包含 初始化程序


- [class.static.data] p4

Just some additional info:

C++ allows to "define" const static types of integral and enumeration types as class members. But this is actually not a definition, just an "initializiation-marker"

You should still write a definition of your member outside of the class.

If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.
The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.

- [class.static.data] p4

尾戒 2024-07-15 17:32:29

使用 C++11,上述内容对于基本类型是可能的,因为

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

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

With C++11, the above would be possible for basic types as

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. The approach proved a bit wobbly with C-string constexprs inside template classes, though.

自在安然 2024-07-15 17:32:29

不知道为什么强制转换有效,但是 Foo::MEMBER 直到第一次加载 Foo 时才被分配,并且由于您从未加载它,所以它永远不会被分配。 如果您在某处引用了 Foo,它可能会起作用。

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. If you had a reference to a Foo somewhere, it would probably work.

旧故 2024-07-15 17:32:29

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

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

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. Once you call static_cast, a temporary variable is created. And a reference to this object can be passed, everything works just fine.

Or at least my colleague who resolved this said so.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文