4

我们一直在 Linux (gcc) 和 Windows (Visual Studio) 上编译一个库,正如预期的那样,发现在两个平台上进行干净编译所需的差异很小,但没有显着差异。

今天,我将 gcc 编译器标志更改为使用-fPIC(以启用共享库)。当我们测试将程序与库链接时,我们开始收到错误(第一次),其中有 undefined reference2 个静态常量在头文件中声明和初始化(但不在 .cpp 文件中)。

我发现this StackOverflow answer似乎解决了这个问题,解释说,即使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 = '\002';
    // 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
4

1 回答 1

2

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

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

因此,对于整数类型(例如intchar在您的示例中),您可以在类定义中进行初始化。如果这样做,您还必须在命名空间范围内(即在您的.cpp文件中)定义它并且使用初始化程序。在规范的该部分的其他地方,声明static其类中的数据成员不是定义,因此必须伴随命名空间范围内的定义,这就是您在示例中所做的。

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

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

于 2013-08-06T18:18:23.210 回答