对静态 const int 的未定义引用
我今天遇到了一个有趣的问题.考虑这个简单的例子:
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();
}
编译时出现错误:
Undefined reference to 'Bar::kConst'
现在,我很确定这是因为 static const int
没有在任何地方定义,这是故意的,因为根据我的理解,编译器应该能够在编译时进行替换 -时间,不需要定义.但是,由于该函数采用 const int &
参数,因此它似乎没有进行替换,而是更喜欢引用.我可以通过进行以下更改来解决此问题:
Now, I'm pretty sure that this is because the static const int
is 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:
foo(static_cast<int>(kConst));
我相信这现在迫使编译器创建一个临时的 int,然后传递一个对它的引用,它可以在编译时成功地做到这一点.
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.
我想知道这是故意的,还是我对 gcc 的期望过高而无法处理这种情况?或者这是我出于某种原因不应该做的事情?
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?
推荐答案
这是故意的,9.4.2/4 说:
It's intentional, 9.4.2/4 says:
如果静态数据成员是 const 整型或 const 枚举类型,它在类中的声明定义可以指定一个常量初始化器,它应该是一个积分常数表达式 (5.19) In在这种情况下,该成员可以出现在积分常数表达式.这成员仍应定义在命名空间范围,如果它在程序
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
当你通过常量引用传递静态数据成员时,你使用"了它,3.2/2:
When you pass the static data member by const reference, you "use" it, 3.2/2:
一个表达式可能被评估除非它出现在一个积分需要常量表达式(请参阅5.19),是 sizeof 运算符 (5.3.3) 的操作数,或者是typeid 运算符和表达式不指定左值多态类类型 (5.2.8).一个对象或非重载函数是如果其名称出现在潜在评估表达式.
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.
因此,实际上,当您也按值或在 static_cast
中传递它时,您使用"了它.只是 GCC 在一种情况下让您摆脱了困境,而在另一种情况下却没有.
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.
const 引用的实际问题是 foo
有权获取其参数的地址,并将其与另一个调用的参数地址进行比较,例如,存储在一个全球的.由于静态数据成员是唯一的对象,这意味着如果您从两个不同的 TU 调用 foo(kConst)
,则在每种情况下传递的对象的地址必须相同.AFAIK GCC 无法安排,除非对象是在一个(并且只有一个)TU 中定义的.
The practical problem with the const reference is that foo
is 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.
好的,所以在这种情况下 foo
是一个模板,因此定义在所有 TU 中都是可见的,所以也许编译器理论上可以排除它对地址做任何事情的风险.但总的来说,您当然不应该获取不存在对象的地址或引用;-)
OK, so in this case foo
is 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 ;-)
相关文章