3

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.

5
  • Remember #pragma once is not standard. So VS might not support it. Leading to multiple inclusions of header files. Commented May 6, 2013 at 17:47
  • 1
    @Named VS does support #pragma once. Commented May 6, 2013 at 17:51
  • @Angew Yes It seems it does. msdn.microsoft.com/en-us/library/4141z1cx.aspx Commented May 6, 2013 at 17:53
  • In addition to @Agnew's comment I also tried this with standard "guards" just to be sure and the result was the same. Commented May 6, 2013 at 17:53
  • This is still a problem with VS2013. Commented Feb 27, 2014 at 18:09

2 Answers 2

7

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).

Sign up to request clarification or add additional context in comments.

3 Comments

Reading the code again, it looks like you are following this rule. So there shouldn't be any problem.
@user2141130: No, your question is missing the object file name on the very front. Where an error occurs is just as important as the error's description.
Answered, thank you. This also answers my previous question. I'm not sure what SO protocol is here but if you want to answer that with a link to here I will gladly accept the answer.
1

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;

1 Comment

How is clang relevant to Visual C++?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.