结构/类中静态常量的奇怪未定义符号

发布于 2024-10-15 18:55:39 字数 445 浏览 6 评论 0原文

要么我很累,要么发生了一些我没有意识到的奇怪的事情,因为下面的代码在链接时导致 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。在类/结构之外定义静态整数(在这个简单的情况下是全局的)也很好。然而,一旦它们像这样进入内部,链接器就无法找到它们。

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

Either I'm very tired or something weird is happening that I'm not aware of, because the code below is resulting in undefined symbols for Foo::A and Foo::B when linking. This is minimized as much as I could from a larger project, but shows the essence of what I'm looking at.

#include <algorithm>

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

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

Without the std::min function template it works fine, i.e. just return Foo::A. Also fine is when defining the static ints outside a class/struct (global in this simple case). However, as soon as they're inside like this, the linker cannot find them.

Can someone explain what's happening?

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

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

发布评论

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

评论(6

栩栩如生 2024-10-22 18:55:39

需要定义

您提供的代码是非标准的。虽然您可以直接在类中为 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);
}

引用标准可以在 c++ 中的 const 和 static 说明符

为什么有时代码在没有定义的情况下“工作”?

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

我不明白为什么你的源代码属于“链接器”类别,需要剖析 std::min 才能理解这一点。 注意:当我使用 GCC 在线尝试时,它有效,但未检测到错误。

替代方案:使用枚举

另一种替代方案是使用枚举。当您使用不支持 static const int“内联”初始值设定项的旧编译器(例如 Visual Studio 6)时,此版本也可以派上用场。但请注意,使用 std::min 时,您会遇到枚举的其他问题,并且需要使用显式实例化或转换,或者在一个命名枚举中同时包含 A 和 B,如 Nawaz 的答案

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(3.2),则仍应在命名空间范围内定义

关于 odr-use 如下:

3 名称作为潜在求值表达式 ex 出现的变量 x 是 odr 使用的,除非 x 是满足出现在常量表达式 (5.19) 中的要求的对象,并且 ex 是一个表达式 e 的潜在结果集合的元素,其中左值到右值转换 (4.1) 应用于 e,或者 e 是丢弃值表达式(第 5 条)。

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

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

Definition needed

The code you have provided is non-standard. While you can provide initializers for const static int members directly in the class, you still need to provide separate definitions. It is weird, a kind of unexpected, but you are expected to write it like this:

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

Quote from standard can be found in a similar question at const and static specifiers in c++

Why sometimes the code "works" without a definition?

As for why you can often get around even without providing the definition: if you are using those members only in constant expressions, compiler will always resolve them directly and there will be no access left for linker resolution. It is only when you use it in some way which cannot be handled by compiler directly, and only in such case the linker will detect the symbol is undefined. I guess this is probably a bug in the Visual Studio compiler, but given the nature of the bug I doubt it will be ever fixed.

Why your source falls into the "linker" category is something I do not see, one would need to dissect the std::min to understand that. Note: When I have tried it online with GCC, it worked, the error was not detected.

Alternative: use enum

Another alternative is to use enum. This version can also come handy when you hit an old compiler which does not support static const int "inline" initializers (such as was Visual Studio 6). Note however that with std::min you are hitting other problems with enums and you need to use an explicit instantiation or casting, or have both A and B in one named enum as in the answer by Nawaz:

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

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

Standards

Note: even Stroustrup C++ FAQ gets this wrong and does not require the definition as strictly as the standard does:

You can take the address of a static member if (and only if) it has an out-of-class definition

The definition is required by a standard in 9.4.2:

C++03 wording:

The member shall still be defined in a name-space scope if it is used in the program and the namespace scope definition shall not contain an initializer

C++11 wording of 9.4.2 is a bit different:

3 The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program

3.2 says following about odr-use:

3 A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless x is an object that satisfies the requirements for appearing in a constant expression (5.19) and ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).

4 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

I have to admit I am not sure what the exact implications of C++11 wording are, as I fail to understand the odr-use rules.

辞取 2024-10-22 18:55:39

如果您只想要整数值,那么您也可以定义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

If you just want integral values,then you can define enum as well:

#include <algorithm>

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

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

This is more than enough. No declaration needed outside the class!

Online Demo : http://www.ideone.com/oE9b5

为人所爱 2024-10-22 18:55:39

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

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

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

You must define the static constants outside the class definition.

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

const int Foo::A = 1;
const int Foo::B = 2;
初见 2024-10-22 18:55:39

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

您可能会在未优化的构建中得到这个,对吗?

我敢打赌,如果您启用优化,您将不会通过 gcc 得到这个。对 std::min() 的调用将被内联,引用将消失。

另外,如果您要在调用 std::min() 之前将 Foo::AFoo::B 分配给两个局部变量code>,这个问题也会消失。

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

There are good answers here, but one additional thing to note is that the parameters of std::min() are references, which is what requires the addresses of the passed in variables, and since those variables don't make it to the object file for the compilation unit, the linker cannot resolve their addresses.

You are probably getting this in an non-optimized build, correct?

I bet that you won't get this with gcc if you enable optimizations. The call to std::min() will get inlined and the references will go away.

Also, if you were to assign Foo::A and Foo::B to two local variables right before the call to std::min(), this issue would also go away.

This isn't ideal, but if you don't own the code that defines the variables that are causing you this issue, then this is something you can consider.

魄砕の薆 2024-10-22 18:55:39

鉴于您基本上使用 struct 作为命名空间,为什么不只使用命名空间:

#include <algorithm>

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

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

Seeing as you're basically using struct as a namespace, why not just use namespace:

#include <algorithm>

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

int main()
{
    return std::min(Foo::A, Foo::B);
}
何其悲哀 2024-10-22 18:55:39

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

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

Another solution would be to inline your static variables, like this it will be available in the final translation unit which does away with the Undefined Symbols error.

Note this will only work in post C++11 AFAIK.

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