结构/类中的静态常量的奇怪未定义符号 [英] Weird undefined symbols of static constants inside a struct/class

查看:186
本文介绍了结构/类中的静态常量的奇怪未定义符号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我非常疲劳或发生奇怪的事情,我不知道,因为下面的代码导致未定义的符号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函数模板, strong>,即只返回Foo :: A。也很好的是定义一个类/结构外的静态int(在这个简单的情况下为全局)。



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

解决方案

需要定义



您提供的代码是非标准的。虽然可以直接在类中为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和静态说明符



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



至于为什么你经常可以绕过即使没有提供定义:如果你使用那些成员只有在常量表达式中,编译器将总是直接解析它们,并且没有链接器解析的访问权限。它只有当你以某种方式使用它不能由编译器直接处理,只有在这种情况下,链接器将检测到符号是未定义的。我猜这可能是Visual Studio编译器中的一个错误,但鉴于错误的性质,我怀疑它会被修复。



为什么你的源码落入链接器类是我看不到的东西,需要剖析std :: min来理解。 注意:当我尝试在线与GCC 时,它工作,未检测到错误。

备选:使用枚举



另一个选择是使用枚举。当你打开一个不支持static const intinline初始化器(例如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 ++常见问题也会出现错误,并且不需要像标准一样严格的定义:


您可以获取静态成员的地址if(且仅当它具有超类定义


标准在9.4.2中的定义



C ++ 03措辞:


如果在程序中使用,成员仍然在命名空间范围内定义并且命名空间范围定义不应包含初始化器


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


3 如果在程序中使用odr(3.2),成员仍然在命名空间范围内定义


3.2说下面关于odr使用:


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



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


我不得不承认,我不确定C ++ 11措辞的确切含义是什么,了解odr使用规则。


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?

解决方案

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.

这篇关于结构/类中的静态常量的奇怪未定义符号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆