Visual Studio 2010 C ++是否完全支持类内const变量?

时间:2013-05-06 17:42:21

标签: c++ visual-studio gcc scope initialization

此问题与先前提出的问题here密切相关。

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


e.g。

这是类定义:

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:"Debug\a.exe.intermediate.manifest" 
/ALLOWISOLATION 
/MANIFESTUAC:"level='asInvoker' uiAccess='false'" 
/DEBUG 
/PDB:"Debug\a.pdb" 
/PGD:"Debug\a.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"Debug\sndbx.pch" /Fa"Debug\" /Fo"Debug\" /Fd"Debug\vc100.pdb" /Gd
/analyze- /errorReport:queue 

再次,这与GCC(g ++ a.cpp b.cpp)成功构建,链接和运行。我提供的代码已经完成,因此可以复制,粘贴和运行。

2 个答案:

答案 0 :(得分:7)

您不应将非模板数据定义放在头文件中,因为在链接时会出现多个定义错误。变量是成员变量还是静态成员变量或const静态成员变量都不会改变它。

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


这实际上看起来像一个Visual C ++错误。如果您已经显示完整的错误消息,那将会有很大的帮助,即

  

b.obj:错误LNK2005:已在a.obj中定义的“public:static int const B :: m_b”(?m_b @ B @@ 2HB)   a.exe:致命错误LNK1169:找到一个或多个多重定义的符号

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


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

  

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


最后,Visual C ++显然正在定义符号(错误地):

R:\ 16404173&gt; dumpbin / symbols b.obj Microsoft(R)COFF / PE Dumper版本10.00.40219.01 版权所有(C)Microsoft Corporation。保留所有权利。

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注释使得简单的情况(头文件中的初始化)工作,但是当提供真正的定义时,这种hackish解决方法就会崩溃。


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

答案 1 :(得分:1)

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

因此,在您的标题中,您需要这样:

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

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

const int B::m_b = 100;