Does Visual Studio 2010 C++ Fully Support in-Class

2019-07-13 12:39发布

问题:

This question is closely related to a question that was previously asked here.

In order for the Visual Studio 2010 C++ debugger to resolve in-class-initialized const variables, a global definition for the variable must be supplied.


e.g.

Here is the class definition:

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

Here is the global scope definition of the member:

const int B::m_b;

Without the global definition the code works but the debugger cannot see m_b within B's methods.

However, this leads to another problem. In non-trivial header file inclusion arrangements (full code given below), Visual Studio produces this link error:

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

However GCC compiles, links, and runs the code successfully.

Here is the code in question:

file a.cpp:

#include <iostream>
#include "a.h"

const int B::m_b;

int main()
{
    B b;
    std::cout << b.m_b;
    return 0;
}

file a.h:

#pragma once

#include "b.h"

file b.cpp:

#include "b.h"

file b.h:

#pragma once

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

Here are the linker options (default VS10 console program):

/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 

Here are the compiler options (default VS10 console program):

/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 

Again, this builds, links, and runs successfully with GCC ( g++ a.cpp b.cpp ). The code I've supplied is complete so it can be copied, pasted, and run.

回答1:

You shouldn't put non-template definitions of data in header files, because you will get multiple definition errors at link time. Whether the variable is a member variable or a static member variable or a const static member variable doesn't change this at all.

Put the definition in exactly one compilation unit, just like you would for any other singleton.


This actually looks like a Visual C++ bug. It would have helped considerably if you had shown the complete error message, namely

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

The symbol would not be defined anywhere in b.obj if the compiler were working right.


Furthermore, attempting to use __declspec(selectany) tells us that the compiler knows this is not a definition:

r:\16404173\b.h(3) : error C2496: 'B::m_b' : 'selectany' can only be applied to data items with external linkage


Finally, Visual C++ clearly is defining the symbol (incorrectly):

R:\16404173>dumpbin /symbols b.obj Microsoft (R) COFF/PE Dumper Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved.

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)

Furthermore, we see that it is marked as selectany already. But in the compilation unit with the actual definition:

    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)

The (pick any) annotation is gone. Which is proper, this is an authoritative definition of the variable.

It's wrong for the variable to appear in b.obj at all. The compiler makes the simple case (initialization inside header file) work by using the selectany annotation, but this hackish workaround falls apart when a real definition is provided.


Finally, adding __declspec(selectany) on the real definition, in a.cpp, works around the link error. But I'm afraid the symbol will still be optimized away by the linker and not available during debugging. You can temporarily use the /OPT:NOREF to avoid this (but it will bloat your executable, so turn that option off again before shipping).



回答2:

According to what clang will accept, the static const variable initialisation must happen in the definition rather than the declaration.

Therefore in your header you want this:

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

And then the definition in your cpp should be like this:

const int B::m_b = 100;