Visual Studio 2010 c++完全支持类内const变量吗?

Does Visual Studio 2010 C++ Fully Support in-Class const Variables?

本文关键字:const 变量 支持 2010 Studio c++ Visual      更新时间:2023-10-16

这个问题与之前问过的一个问题密切相关。

为了使Visual Studio 2010 c++调试器能够解析类内初始化的const变量,必须为该变量提供一个全局定义


下面是类定义:

class B{
  public:
   static const int m_b=100;
};

成员的全局作用域定义:

const int B::m_b;

如果没有全局定义,代码可以工作,但调试器无法在B的方法中看到m_b。

然而,这导致了另一个问题。在重要的头文件包含安排(完整代码如下所示)中,Visual Studio会产生以下链接错误:

error LNK2005: "public: static int const B::m_b" (?m_b@B@@2HB) already defined in a.obj
1>a.exe : fatal error LNK1169: one or more multiply defined symbols found

但是GCC会成功编译、链接并运行代码。

下面是问题代码:

文件a.cpp:

#include <iostream>
#include "a.h"
const int B::m_b;
int main()
{
    B b;
    std::cout << b.m_b;
    return 0;
}

文件a.h:

#pragma once
#include "b.h"

文件b.cpp:

#include "b.h"

文件b.h:

#pragma once
class B {
public:
    static const int m_b = 100;
};
以下是链接器选项(默认的VS10控制台程序):
/OUT:"a.exe" 
/NOLOGO "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" 
/MANIFEST 
/ManifestFile:"Debuga.exe.intermediate.manifest" 
/ALLOWISOLATION 
/MANIFESTUAC:"level='asInvoker' uiAccess='false'" 
/DEBUG 
/PDB:"Debuga.pdb" 
/PGD:"Debuga.pgd" 
/TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE 
以下是编译器选项(默认VS10控制台程序):
/ZI /nologo /W3 /WX- /Od /Oy- /D "_MBCS" /Gm /EHsc /RTC1 /GS /fp:precise /Zc:wchar_t
/Zc:forScope /Fp"Debugsndbx.pch" /Fa"Debug" /Fo"Debug" /Fd"Debugvc100.pdb" /Gd
/analyze- /errorReport:queue 

同样,它使用GCC (g++ a.p pp b.p)构建、链接并成功运行。我提供的代码是完整的,因此可以复制、粘贴和运行。

你不应该把数据的非模板定义放在头文件中,因为你会在链接时得到多个定义错误。无论变量是成员变量、静态成员变量还是const静态成员变量都不会改变这一点。

将定义放在一个编译单元中,就像处理任何其他单例一样。


这实际上看起来像一个Visual c++ bug。如果您显示完整的错误消息,即

,将会有很大帮助。

b。obj:错误LNK2005: "public: static int B::m_b" (?m_b@B@@2HB)已经在a.obj中定义.exe:致命错误LNK1169:发现一个或多个乘法定义符号

如果编译器工作正常,该符号不会在b.b obj中的任何地方定义。


此外,尝试使用__declspec(selectany)告诉我们编译器知道这不是定义:

r:16404173b.h(3):错误C2496: 'B::m_b': 'selectany'只能应用于带有外部链接的数据项


最后,Visual c++清楚地定义了符号(不正确):

R:16404173>dumpbin/symbols b.objMicrosoft (R) COFF/PE dump版本10.00.40219.01微软公司版权所有。版权所有

Dump of file b.obj
File Type: COFF OBJECT
COFF SYMBOL TABLE
000 00AB9D1B ABS    notype       Static       | @comp.id
001 00000000 SECT1  notype       Static       | .drectve
    Section length   2F, #relocs    0, #linenums    0, checksum        0
003 00000000 SECT2  notype       Static       | .debug$S
    Section length   64, #relocs    0, #linenums    0, checksum        0
005 00000000 SECT3  notype       Static       | .rdata
    Section length    4, #relocs    0, #linenums    0, checksum B4446054, selection    2 (pick any)
007 00000000 SECT3  notype       External     | ?m_b@B@@2HB (public: static int const B::m_b)

此外,我们看到它已经被标记为selectany。但是在编译单元中有实际的定义:

    Section length    4, #relocs    0, #linenums    0, checksum B4446054
229 00000000 SECTB9 notype       External     | ?m_b@B@@2HB (public: static int const B::m_b)

没有(pick any)标注。这是正确的,这是对变量的权威定义。

b.obj中出现变量是错误的。编译器通过使用selectany注释使简单的情况(头文件中的初始化)工作,但是当提供了真正的定义时,这种粗糙的解决方案就崩溃了。


最后,在a.p中,在实际定义上添加__declspec(selectany),可以解决链接错误。但是我担心这个符号仍然会被链接器优化掉,并且在调试期间不可用。您可以暂时使用/OPT:NOREF来避免这种情况(但它会使您的可执行文件膨胀,因此在发布之前再次关闭该选项)。

根据clang将接受的内容,静态const变量的初始化必须在定义中而不是在声明中进行。

因此在你的页眉中你需要这样写:

class B{
  public:
   static const int m_b;
};

然后在你的cpp中的定义应该是这样的:

const int B::m_b = 100;