C++ 对静态 const int 的未定义引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5391973/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:03:43  来源:igfitidea点击:

Undefined reference to static const int

c++gcc

提问by JaredC

I ran into an interesting issue today. Consider this simple example:

我今天遇到了一个有趣的问题。考虑这个简单的例子:

template <typename T>
void foo(const T & a) { /* code */ }

// This would also fail
// void foo(const int & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

int main()
{
   Bar b;
   b.func();
}

When compiling I get an error:

编译时出现错误:

Undefined reference to 'Bar::kConst'

Now, I'm pretty sure that this is because the static const intis not defined anywhere, which is intentional because according to my understanding the compiler should be able to make the replacement at compile-time and not need a definition. However, since the function takes a const int &parameter, it seems to be not making the substitution, and instead preferring a reference. I can resolve this issue by making the following change:

现在,我很确定这是因为static const int没有在任何地方定义,这是故意的,因为根据我的理解,编译器应该能够在编译时进行替换,而不需要定义。然而,由于该函数接受一个const int &参数,它似乎没有进行替换,而是更喜欢引用。我可以通过进行以下更改来解决此问题:

foo(static_cast<int>(kConst));

I believe this is now forcing the compiler to make a temporary int, and then pass a reference to that, which it can successfully do at compile time.

我相信这现在迫使编译器创建一个临时的 int,然后传递一个对它的引用,它可以在编译时成功完成。

I was wondering if this was intentional, or am I expecting too much from gcc to be able to handle this case? Or is this something I shouldn't be doing for some reason?

我想知道这是故意的,还是我对 gcc 的期望过高而无法处理这种情况?或者这是我出于某种原因不应该做的事情?

采纳答案by Steve Jessop

It's intentional, 9.4.2/4 says:

这是故意的,9.4.2/4 说:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19) In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program

如果静态数据成员是 const 整型或 const 枚举类型,则其在类定义中的声明可以指定一个常量初始化器,它应该是整型常量表达式 (5.19) 在这种情况下,该成员可以出现在整型常量表达式中。如果在程序中使用该成员仍应在命名空间范围内定义

When you pass the static data member by const reference, you "use" it, 3.2/2:

当您通过 const 引用传递静态数据成员时,您“使用”它,3.2/2:

An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19), is the operand of the sizeof operator (5.3.3), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8). An object or non-overloaded function is used if its name appears in a potentially-evaluated expression.

表达式可能会被求值,除非它出现在需要整型常量表达式的地方(见 5.19),是 sizeof 运算符(5.3.3)的操作数,或者是 typeid 运算符的操作数,并且表达式没有指定左值多态类类型 (5.2.8)。如果对象或非重载函数的名称出现在潜在求值表达式中,则使用该对象或非重载函数。

So in fact, you "use" it when you pass it by value too, or in a static_cast. It's just that GCC has let you off the hook in one case but not the other.

所以事实上,当你也按值传递它时,你“使用”它,或者在static_cast. 只是 GCC 在一种情况下让您摆脱困境,而在另一种情况下却没有。

[Edit: gcc is applying the rules from C++0x drafts: "A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.". The static cast performs lvalue-rvalue conversion immediately, so in C++0x it's not "used".]

[编辑:gcc 正在应用 C++0x 草案中的规则:“名称作为潜在求值表达式出现的变量或非重载函数是 odr 使用的,除非它是满足出现在常量中的要求的对象表达式 (5.19) 和左值到右值的转换 (4.1) 会立即应用。”。静态强制转换立即执行左值-右值转换,因此在 C++0x 中它不被“使用”。]

The practical problem with the const reference is that foois within its rights to take the address of its argument, and compare it for example with the address of the argument from another call, stored in a global. Since a static data member is a unique object, this means if you call foo(kConst)from two different TUs, then the address of the object passed must be the same in each case. AFAIK GCC can't arrange that unless the object is defined in one (and only one) TU.

const 引用的实际问题foo是它有权获取其参数的地址,并将其与存储在全局中的另一个调用的参数地址进行比较。由于静态数据成员是唯一的对象,这意味着如果您foo(kConst)从两个不同的 TU调用,则在每种情况下传递的对象的地址必须相同。AFAIK GCC 无法安排,除非对象是在一个(并且只有一个)TU 中定义的。

OK, so in this case foois a template, hence the definition is visible in all TUs, so perhaps the compiler could in theory rule out the risk that it does anything with the address. But in general you certainly shouldn't be taking addresses of or references to non-existent objects ;-)

好的,所以在这种情况下foo是一个模板,因此定义在所有 TU 中都是可见的,所以也许编译器理论上可以排除它对地址做任何事情的风险。但总的来说,您当然不应该获取不存在对象的地址或引用;-)

回答by pelya

If you're writing static const variable with initializer inside class declaration it's just like as if you've written

如果您在类声明中使用初始化程序编写静态常量变量,就好像您已经编写了一样

class Bar
{
      enum { kConst = 1 };
}

and GCC will treat it the same way, meaning that it does not have an address.

并且 GCC 会以同样的方式对待它,这意味着它没有地址。

The correct code should be

正确的代码应该是

class Bar
{
      static const int kConst;
}
const int Bar::kConst = 1;

回答by Stac

This is a really valid case. Especially because foocould be a function from the STL like std::countwhich takes a const T&as its third argument.

这是一个非常有效的案例。特别是因为foo可能是来自 STL 的函数,如std::count,它将const T&作为其第三个参数。

I spent much time trying to understand why the linker had problems with such a basic code.

我花了很多时间试图理解为什么链接器在使用这样一个基本代码时会出现问题。

The error message

错误信息

Undefined reference to 'Bar::kConst'

对“Bar::kConst”的未定义引用

tells us that the linker cannot find a symbol.

告诉我们链接器找不到符号。

$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 U Bar::kConst

We can see from the 'U' that Bar::kConst is undefined. Hence, when the linker tries to do its job, it has to find the symbol. But you only declarekConst and don't define it.

我们可以从 'U' 看到 Bar::kConst 是未定义的。因此,当链接器试图完成它的工作时,它必须找到符号。但是你只声明了kConst 而没有定义它。

The solution in C++ is also to define it as follows:

C++中的解决方案也是如下定义:

template <typename T>
void foo(const T & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

const int Bar::kConst;       // Definition <--FIX

int main()
{
   Bar b;
   b.func();
}

Then, you can see that the compiler will put the definition in the generated object file:

然后,您可以看到编译器会将定义放入生成的目标文件中:

$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 R Bar::kConst

Now, you can see the 'R' saying that it is defined in the data section.

现在,您可以看到 'R' 表示它是在数据部分中定义的。

回答by Yoav

You can also replace it by a constexpr member function:

您还可以将其替换为 constexpr 成员函数:

class Bar
{
  static constexpr int kConst() { return 1; };
};

回答by TonyK

g++ version 4.3.4 accepts this code (see this link). But g++ version 4.4.0 rejects it.

g++ 4.3.4 版接受此代码(请参阅此链接)。但是 g++ 4.4.0 版拒绝了它。

回答by quamrana

I think this artefact of C++ means that any time that Bar::kConstis referred to, its literal value is used instead.

我认为 C++ 的这种人工制品意味着任何时候都Bar::kConst被引用,而是使用它的字面值。

This means that in practise there is no variable to make a reference point to.

这意味着实际上没有变量可供参考。

You may have to do this:

您可能必须这样做:

void func()
{
  int k = kConst;
  foo(k);
}

回答by Ethouris

Simple trick: use +before the kConstpassed down the function. This will prevent the constant from being taken a reference from, and this way the code will not generate a linker request to the constant object, but it will go on with the compiler-time constant value instead.

简单的技巧:+kConst传递下来的函数之前使用。这将防止常量被引用,这样代码就不会生成对常量对象的链接器请求,而是继续使用编译器时常量值。

回答by Scg

I experienced the same problem as mentioned by Cloderic (static const in a ternary operator: r = s ? kConst1 : kConst2), but it only complained after turning off compiler optimization (-O0 instead of -Os). Happened on gcc-none-eabi 4.8.5.

我遇到了与 Cloderic 提到的相同的问题(三元运算符中的静态常量:)r = s ? kConst1 : kConst2,但它只是在关闭编译器优化(-O0 而不是 -Os)后才抱怨。发生在 gcc-none-eabi 4.8.5 上。