Linux gcc和Windows Visual Studio处理静态常量的区别

Difference between Linux gcc and Windows Visual Studio handling of static constants?

本文关键字:静态 常量 区别 处理 Studio gcc Windows Visual Linux      更新时间:2023-10-16

我们一直在Linux(gcc)和Windows(Visual Studio)上编译一个库,正如预期的那样,在这两个平台上获得干净编译所需的内容之间存在微小但不显著的差异。

今天,我将gcc编译器标志更改为使用-fPIC(以便能够创建共享库)。当我们针对库测试链接程序时,我们开始出现错误(第一次),undefined reference到2个静态常量在头文件中声明和初始化(但不在.cpp文件中)。

我找到了这个StackOverflow的答案,它似乎解决了这个问题,解释说,即使static const在头文件中初始化,它仍然需要在代码文件中定义。进行该更改确实删除了gcc链接器错误。

然而,Visual Studio不喜欢这种更改,并生成了multiple definition错误。我们必须包装需要一个预处理器条件的定义,以使Visual Studio能够干净地编译。

有人能告诉我这里有什么不同吗?(代码摘录如下。)

消息.h

class msg
{
  public:
    static const int EMPTY_INT_VALUE = INT_MAX;
    static const char EMPTY_STRING_VALUE = '02';
    // can't define value in header, defined in cpp file
    static const double EMPTY_DOUBLE_VALUE;   
    ...
}

消息.cpp

#include "msg.h"
const double msg::EMPTY_DOUBLE_VALUE(DBL_MAX);
#ifndef _WIN32
// g++ requires these definitions, vs 2010 doesn't like them
const int msg::EMPTY_INT_VALUE;
const char msg::EMPTY_STRING_VALUE;
#endif

我已经追踪到C++语言规范(INCITS/ISO/IEC 14882-2011[2012])第9.4.2节"静态数据成员":

如果非易失性const static数据成员是整型或枚举型,则其在类定义中的声明可以指定大括号或相等初始化器,其中作为赋值表达式的每个初始化器子句都是常量表达式(5.19)。文本类型的static数据成员可以在类定义中将constexpr说明符声明;如果是,则其声明应指定大括号或相等的初始值设定项,其中作为赋值表达式的每个初始值设定子句都是常量表达式。[注意:在这两种情况下,成员都可能出现在常量表达式中。--结束注释]如果在程序中使用odr(3.2),则该成员仍应在命名空间范围中定义,并且命名空间范围定义不应包含初始值设定项

因此,对于积分类型(例如,示例中的intchar),可以在类定义中初始化。如果这样做,还必须在命名空间范围内(即在.cpp文件中)定义它,并且不带初始值设定项。在规范的其他部分中,声明了static数据成员在其类中的声明是而不是定义,因此必须伴随着命名空间范围的定义,这就是您在示例中所做的。

GCC遵循规范的这一部分,而Visual C++则没有。我使用Visual Studio 2012进行了测试。

以下是另一个关于Visual C++在这方面的不合规性的讨论。这里描述的解决方法正是您正在做的:保护积分变量的定义,尽管它们使用的是_MSC_VER