struct / class中静态常量的奇怪未定义符号

时间:2011-02-03 20:03:40

标签: c++ initialization definition static-members undefined-symbol

我非常疲倦或者发生了一些我不知道的奇怪事情,因为下面的代码导致Foo :: A和Foo :: B链接时未定义的符号 。这可以从一个更大的项目中尽可能地减少,但显示了我正在看的内容的本质。

#include <algorithm>

struct Foo
{
    static const int A = 1;
    static const int B = 2;
};

int main()
{
    return std::min(Foo::A, Foo::B);
}

没有std :: min函数模板,它可以正常工作,即只返回Foo :: A.在类/结构之外定义静态int时也很好(在这个简单的例子中是全局的)。但是,只要他们像这样,链接器就找不到它们。

有人可以解释发生了什么吗?

6 个答案:

答案 0 :(得分:42)

需要定义

您提供的代码是非标准的。虽然您可以直接在类中为const static int成员提供初始化程序,但仍需要提供单独的定义。这很奇怪,有点出乎意料,但你应该这样写:

#include <algorithm>

struct Foo
{
    static const int A = 1;
    static const int B = 2;
};

const int Foo::A;
const int Foo::B;

int main()
{
    return std::min(Foo::A, Foo::B);
}

标准报价可以在const and static specifiers in c++

的类似问题中找到

为什么有时代码“没有定义”?

至于为什么你甚至可以在没有提供定义的情况下解决这个问题:如果你只在常量表达式中使用这些成员,编译器将始终直接解析它们,并且将不会留下链接器解析的访问权限。只有当您以某种方式使用它时,编译器才能直接处理它,并且只有在这种情况下,链接器才会检测到符号未定义。我想这可能是Visual Studio编译器中的一个错误,但考虑到错误的性质,我怀疑它是否会被修复。

为什么你的源代码属于“链接器”类别是我看不到的,需要解析std :: min来理解它。 注意:当我尝试online with GCC时,它有效,但未检测到错误。

替代方案:使用枚举

另一种选择是使用枚举。当你遇到一个不支持静态const int“inline”初始化器(比如Visual Studio 6)的旧编译器时,这个版本也会派上用场。但是请注意,使用std :: min,您将遇到枚举的其他问题,您需要使用显式实例化或强制转换,或者在the answer by Nawaz中将A和B都放在一个命名枚举中:

struct Foo
{
    enum {A = 1};
    enum {B = 2};
};

int main()
{
    return std::min<int>(Foo::A, Foo::B);
}

标准

注意:即使Stroustrup C++ FAQ出错也不会像标准那样严格要求定义:

  

如果(并且仅当)它具有类外定义,您可以获取静态成员的地址

9.4.2中的标准要求 定义

C ++ 03措辞:

  

如果在程序中使用该成员,并且命名空间作用域定义不包含初始化程序,则该成员仍应在名称空间作用域中定义

9.4.2的C ++ 11措辞略有不同:

  

3 如果程序中使用了odr-used(3.2),该成员仍应在命名空间作用域中定义

3.2说关于使用odr:

  

3 变量x的名称显示为可能被评估的表达式ex是odr-used,除非x是满足出现在常量表达式(5.19)中的要求的对象,ex是表达式e的潜在结果集合的元素,其中左值到右值的转换(4.1)应用于e,或者e是丢弃值表达式(第5条)。

     

4 每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义;无需诊断。

我不得不承认我不确定C ++ 11措辞的确切含义是什么,因为我无法理解使用规则。

答案 1 :(得分:3)

如果您只想要积分值,那么您也可以定义enum

#include <algorithm>

struct Foo
{
    enum integrals { A = 1, B = 2} ;
};

int main()
{
    return std::min(Foo::A, Foo::B);
}

这已经足够了。课外不需要声明!

在线演示:http://www.ideone.com/oE9b5

答案 2 :(得分:2)

您必须在类定义之外定义静态常量。

struct Foo {
    static const int A;
    static const int B;
};

const int Foo::A = 1;
const int Foo::B = 2;

答案 3 :(得分:2)

这里有很好的答案,但需要注意的另一件事是std::min()的参数是引用,这需要传入的变量的地址,并且因为这些变量不能实现编译单元的目标文件,链接器无法解析其地址。

你可能在非优化版本中得到这个,对吗?

我敢打赌,如果启用优化,你将不会使用gcc。对std::min()的调用将被内联,参考文献将会消失。

此外,如果您在调用Foo::A之前将Foo::Bstd::min()分配给两个本地变量,此问题也会消失。

这并不理想,但是如果您不拥有定义导致此问题的变量的代码,那么您可以考虑这一点。

答案 4 :(得分:1)

看到你基本上使用struct作为名称空间,为什么不使用namespace:

#include <algorithm>

namespace Foo
{
    const int A = 1;
    const int B = 2;
};

int main()
{
    return std::min(Foo::A, Foo::B);
}

答案 5 :(得分:0)

另一种解决方案是inline您的static变量,像这样,它将在最终翻译单元中可用,从而消除了未定义符号错误。

请注意,这仅适用于C ++ 11 AFAIK之后的版本。

相关问题