c – 静态Const成员初始化和模板(与静态功能相比) – 这是如何工作的?

我知道C标准说(第9.4.2段第4段),整数或枚举类型的静态成员变量可以在类中提供初始化器,但这需要在类外部定义该成员(在编译单元中) ).即,你需要做这样的事情:

class A
{
public:
    static const int X = 10;
};

// this is required by the standard
const int A::X;

我已经看到(我已经看到它说其他地方)一些编译器会让你在没有外部定义的情况下逃脱.这适用于OS X上的gcc 4.2.1:

#include <iostream>    

class A
{
public:
    static const int X = 10;
};

int main(int argc, char** argv)
{
    std::cout << A::X << std::endl;
    return 0;
}

我最近遇到过一个人做过这个的错误,但他们在模板化函数中使用了成员变量(std :: max是准确的),并且它不会编译,抱怨未定义的符号A :: X.即,这不起作用:

#include <iostream> 
#include <algorithm>   

class A
{
public:
    static const int X = 10;
};

int main(int argc, char** argv)
{
    std::cout << std::max(1, A::X) << std::endl;
    return 0;
}

添加类外定义使其有效.

这是一个学术问题,但我想知道为什么会这样.特别是关于以下事实:如果我们用静态函数替换静态成员变量(static const int X = 10;变成静态int X(),A :: X变成A :: X()),那么它将编译没有外部定义.我提到模板的原因是因为std :: max是模板化的,其他模板化函数会重现相同的行为.它可能与模板没有特别的关系,但我想了解为什么模板会导致它们的行为.我认为这必须与模板和静态成员编译/实现的方式有关?

PS – 我在github上发布了一些最小代码

解决方法:

它将在没有定义的情况下编译.

如果静态成员变量使用了odr,则在链接时需要定义. (如果编译器在每次引用时都设法替换它的实际值,那就不会使用它.另一方面,取其地址肯定会使它使用得多)

这是完整的规则(第9.4.2节[class.static.data]):

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.

从第3.2节[basic.def.odr]:

A variable 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 and the lvalue-to-rvalue conversion is immediately applied.

出现在常量表达式中的要求得到满足,因此一切都取决于它是用作左值还是右值.

std :: max采用左值引用,因此没有立即的左值到右值转换.

与模板的唯一交互是可能存在多个等效定义,链接器将选择任何一个.在您的情况下,没有模板,因此多个定义将产生“符号乘法定义”类型的错误.

当您忘记提供定义时,两种情况(模板类的成员和普通类的成员)都会给出相同的错误:“undefined symbol”.

上一篇:linux – 什么是-lnuma以及什么程序使用它进行编译?


下一篇:c – 使用BOOST线程属性会导致绑定编译错误