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

mur*_*att 32 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);
}
Run Code Online (Sandbox Code Playgroud)

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

有人能解释发生了什么吗?

Sum*_*uma 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);
}
Run Code Online (Sandbox Code Playgroud)

标准引用可以在const和c ++中的静态说明符的类似问题中找到

为什么有时代码"工作"没有定义?

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

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

替代方案:使用枚举

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

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

int main()
{
    return std::min<int>(Foo::A, Foo::B);
}
Run Code Online (Sandbox Code Playgroud)

标准

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

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

该定义由标准9.4.2要求:

C++ 03措辞:

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

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

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

3.2关于使用odr说:

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

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

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

  • 我不知道积分常数需要这个.我看到这种代码很多,不一定是我写的;)这是否意味着我应该永远不应该做我展示的内容并始终在cpp文件中初始化静态常量? (2认同)
  • 可能会发生链接器错误,因为`std :: min`函数通过引用获取参数.因此编译器必须记录使用常量的地址(可能即使函数被内联),以便每个需要常量地址的转换单元获得相同的值(单个对象不能有多个地址)在C++). (2认同)