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

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.

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

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;

Related

What is the Visual Studio warning equivalent of GCC's -Wreturn-type?

Does Visual Studio have a warning (or warnings) that is the equivalent of GCC's -Wreturn-type?
More specifically, I am looking for a Visual Studio warning (or warnings) that will warn for instances in functions whose return types are not void where
There is a return statement with no return value; or
It is possible function execution could "fall off" the end of the function body without returning a value
I am not concerned with the other part of -Wreturn-type that warns whenever a function is defined with a return type that defaults to int.
For reference, the GCC warning options can be found here.
As pointed out in the comments, this can be done with C4033, C4716, C4715.
User n. 1.8e9-where's-my-share m. also makes a very good point about how to find MSVC warnings in general:
If you want to find out whether a warning that you want exists, just enable all [using /Wall] and test against a small piece of code. If there is a warning, congrats, you found it. If not, tough luck, there isn't any.
I test both with .c and .cpp file extensions, just in case the compiler behaved differently based on the language it is compiling (and sure enough, the behaviour was different for test 2).
None of my tests ever complain about main(), because main() is special, as it is the only function in C and C++ that defaults to returning 0 if no explicit return is provided.
All of the tests below were done using Visual Studio 2015's compiler (i.e., C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\cl.exe), and the commands were issued from the VS2015 x86 Native Tools Command Prompt.
If I am missing any test cases, please leave a comment to let me know.
Tests
C tests
Test 1 - Empty function with int return type
test_warnings.c:
int main() {}
int foo() {}
Compile results:
>cl /nologo /W0 /we4033 /we4716 /we4715 C:\Users\Administrator\src\test-code\test_warnings.c
test_warnings.c
c:\users\administrator\src\test-code\test_warnings.c(3) : error C4716: 'foo': must return a value
Test 2 - Function with int return type with a return without a value
test_warnings.c:
int main() {}
int foo() {
return;
}
Compile results:
>cl /nologo /W0 /we4033 /we4716 /we4715 C:\Users\Administrator\src\test-code\test_warnings.c
test_warnings.c
C:\Users\Administrator\src\test-code\test_warnings.c(4): error C4033: 'foo' must return a value
Test 3 - Function with int return type where execution could "fall off" the end of the function
This test demonstrates that these warnings are not enough, as there is no warning or error emitted for this code.
test_warnings.c:
#include <stdlib.h>
#include <time.h>
int main() {}
int foo() {
int rand_num;
srand(time(0));
rand_num = rand();
if (rand_num > 1) {
return 0;
}
}
Compile results:
>cl /nologo /W0 /we4033 /we4716 /we4715 C:\Users\Administrator\src\test-code\test_warnings.c
test_warnings.c
c:\users\administrator\src\test-code\test_warnings.c(14) : error C4715: 'foo': not all control paths return a value
C++ tests
Test 1 - Empty function with int return type
test_warnings.cpp:
int main() {}
int foo() {}
Compile results:
>cl /nologo /W0 /we4033 /we4716 /we4715 C:\Users\Administrator\src\test-code\test_warnings.cpp
test_warnings.cpp
c:\users\administrator\src\test-code\test_warnings.cpp(3) : error C4716: 'foo': must return a value
Test 2 - Function with int return type with a return without a value
test_warnings.cpp:
int main() {}
int foo() {
return;
}
Compile results:
>cl /nologo /W0 /we4033 /we4716 /we4715 C:\Users\Administrator\src\test-code\test_warnings.cpp
test_warnings.cpp
C:\Users\Administrator\src\test-code\test_warnings.cpp(4): error C2561: 'foo': function must return a value
C:\Users\Administrator\src\test-code\test_warnings.cpp(3): note: see declaration of 'foo'
Test 3 - Function with int return type where execution could "fall off" the end of the function
test_warnings.cpp:
#include <stdlib.h>
#include <time.h>
int main() {}
int foo() {
int rand_num;
srand(time(0));
rand_num = rand();
if (rand_num > 1) {
return 0;
}
}
Compile results:
>cl /nologo /W0 /we4033 /we4716 /we4715 C:\Users\Administrator\src\test-code\test_warnings.cpp
test_warnings.cpp
c:\users\administrator\src\test-code\test_warnings.cpp(14) : error C4715: 'foo': not all control paths return a value
Can you get this with just C4715?
I reran my tests above to see if you can get the same behaviour with just C4715, and here are my results. The command I used for testing this was
cl /nologo /W0 /we4715 <path to file>
Test
C
C++
Test 1
No warning or error
Triggers C4716 as an error, even though this is not turned on (which makes sense, as the docs for this warning say it is automatically promoted to error unless #pragma warning is used to prevent this)
Test 2
No warning or error
Triggers C2561 (a compiler error)
Test 3
Triggers C4715
Triggers C4715
This means C4715 is sufficient for C++, but not sufficient for C.
Notes
C4715 may warn if you call a function that never returns. For example, if you call a function that ends with while (true) {} or throw "error message";. To avoid this, declare the function that never returns with __declspec(noreturn), or if you are using C++11 or newer, you can use the more portable [[noreturn]] in the function declaration. (If you are calling a standard library function like exit(), the compiler will not issue a warning because it will know that function never returns.)
For some interesting related discussion, see Why does flowing off the end of a non-void function without returning a value not produce a compiler error?.

C++ : No warning for unused local constants with /W4 (MSVC)

The following code was compiled with MSVC 19.29.30133.0
Given the following c++ line in a function
int foo = 0;
If the variable isn't referenced, MSCV will give you the following warning: warning C4189: 'foo': local variable is initialized but not referenced
However, if const is added in front of it, the warning disappears.
const int foo = 0; // <<--This line yields no warning with MSVC 19.29.30133.0
But this unused variable should still be removed, isn't it?
So why is it that MSVC gives no warning? Is it a bug, is there a specification for unused constants?
minimal reproduction:
int main() {
int a = 0;
const int b = 0;
return 0;
}
output:
Starting build...
cl.exe /Zi /EHsc /nologo /W4 /std:c++20 /Fe: F:\prog\INF1005C\tst\unused_constant.exe F:\prog\INF1005C\tst\unused_constant.cpp
unused_constant.cpp
F:\prog\INF1005C\tst\unused_constant.cpp(2): warning C4189: 'a': local variable is initialized but not referenced
Build finished with warning(s).

Is it a Visual C++ optimizer bug or do I have a bug in my code?

We're in the process of migrating from VS2013 to VS2017.
Below is a probably not so minimal code sample, but it's the best I could do.
The gist of it is that a specific float value is sent to a function, but the function receives the wrong value - this is because of a registers mismatch in the calling function.
This code runs incorrectly on VC141 (VS 2017) and VC140 (VS 2015), but runs correctly on VC120 (VS 2013) and on the clang version which comes built in with VS 2017 (Clang with Microsoft CodeGen (v141_clang_c2) - whatever clang compatible version it is).
The problem manifests when compiling for x64 platform in Release (with optimizations). When removing optimizations the code works fine, so my guess is the optimizer.
The wrong behavior is in badFunc() when calling test().
The code:
#include <iostream>
#include <vector>
struct FloatWrapper
{
FloatWrapper() : m_value(0) {}
explicit FloatWrapper(float value) : m_value(value) {}
float getValue() const { return m_value; }
private:
float m_value;
};
class Tester
{
public:
virtual bool test(FloatWrapper elevation) const
{
std::cout << "Expected=" << m_expected.getValue() << ", received=" << elevation.getValue() << '\n';
return elevation.getValue() == m_expected.getValue();
}
Tester(FloatWrapper expected) : m_expected(expected)
{
}
FloatWrapper m_expected;
};
struct DataBlock
{
FloatWrapper a, b;
};
bool badFunc(const Tester& query, std::vector<DataBlock> blocks)
{
auto block = blocks[0];
if (!query.test(block.b))
{
std::cout << "Tried to send " << block.b.getValue() << '\n';
return false;
}
return true;
}
int main(int argc, const char** argv)
{
DataBlock block;
block.b = FloatWrapper(0.2f);
Tester tester(block.b);
return badFunc(tester, { block }) ? 0 : 1;
}
Compiler command line:
/GS /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /Fd"x64\Release\vc141.pdb" /Zc:inline /fp:precise /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oi /MD /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Fp"x64\Release\compiler_bug_vc14.pch" /diagnostics:classic
Linker command line:
/OUT:"x64\Release\compiler_bug_vc14.exe" /MANIFEST /LTCG:incremental /NXCOMPAT /PDB:"x64\Release\compiler_bug_vc14.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG:FULL /MACHINE:X64 /OPT:REF /INCREMENTAL:NO /PGD:"x64\Release\compiler_bug_vc14.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"x64\Release\compiler_bug_vc14.exe.intermediate.manifest" /OPT:ICF /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
The answer is yes - it is an optimizer bug.
Microsoft say they fixed it and it is currently (24 Sep `17) pending release.
See https://developercommunity.visualstudio.com/content/problem/84976/optimizer-bug-in-vc140141-passing-the-wrong-float.html

Why does Visual Studio fail to give an undefined reference error when extern "C" is specified?

Given this code:
A2.H
_declspec(dllimport) void SomeFunc();
struct Foo
{
Foo();
~Foo();
};
inline Foo::Foo() { }
inline Foo::~Foo()
{
SomeFunc();
}
A1.H
#include "A2.h"
extern "C" void TriggerIssue(); // <-- This!
extern "C" inline void TriggerIssue()
{
Foo f;
}
MyTest.cpp
#include "A1.h"
int main()
{
return 0;
}
Please see here for a background to the issue.
When MyTest.cpp is compiled into an executable, the linker complains that SomeFunc() is an unresolved external.
This seems to be caused because of an extraneous (erroneous?) declaration of
TriggerIssue in A1.h. Commenting that out causes the linker error to go away.
Can someone tell me what's going on here? I just want to understand what specifically causes the compiler to behave differently in the presence and absence of that declaration. The snippet above is my attempt to write a minimally verifiable example of a scenario I am running into. Please don't ask me why its written the way it is.
Note to downvoters: This is NOT a question about how to fix unresolved external symbol errors. So please STOP voting to close this as duplicate. I don't have enough cred to remove that link that keeps showing up at the top of this post claiming this question "may have a possible answer".
The issue is present regardless of the first declaration, and will still be present if you comment out the first declaration and call TriggerIssue() in your program.
It's caused by cl generating the code to call SomeFunc() when it calls Foo's destructor upon TriggerIssue()'s exit, not by any quirk or interaction between the two declarations. The reason it shows up if you don't comment out the non-inline declaration is that the other declaration tells the compiler that you want it to generate a symbol for the function so it can be exported to other modules, which prevents it from actually inlining the code, instead forcing it to generate a normal function. When the function's body is generated, it ends with an implicit call to ~Foo(), which is the source of the issue.
If the non-inline declaration is commented out, however, the compiler will merrily treat the code as inline, and only generate it if you actually call it; since your test program doesn't actually call TriggerIssue(), the code is never generated, and ~Foo() is never called; since the destructor is also inline, this allows the compiler to ignore it entirely and not generate code for it. If you do insert a call to TriggerIssue() in your test program, however, you'll see the exact same error message.
Test #1: Both declarations present.
I compiled your code directly, piping the output to a log file.
cl MyTest.cpp > MyTest.log
The resulting log file was:
MyTest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:MyTest.exe
MyTest.obj
MyTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl SomeFunc(void)" (__imp_?SomeFunc##YAXXZ) referenced in function "public: __thiscall Foo::~Foo(void)" (??1Foo##QAE#XZ)
MyTest.exe : fatal error LNK1120: 1 unresolved externals
Test 2: Non-inline declaration commented out, TriggerIssue() called in main().
I made a couple changes to your code:
// A2.h was unchanged.
// -----
// A1.h:
#include "A2.h"
//extern "C" void TriggerIssue(); // <-- This!
extern "C" inline void TriggerIssue()
{
Foo f;
}
// -----
// MyTest.cpp
#include "A1.h"
int main()
{
TriggerIssue();
return 0;
}
I again compiled the code and piped the results to a log file, using the same command line as before:
MyTest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:MyTest.exe
MyTest.obj
MyTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl SomeFunc(void)" (__imp_?SomeFunc##YAXXZ) referenced in function "public: __thiscall Foo::~Foo(void)" (??1Foo##QAE#XZ)
MyTest.exe : fatal error LNK1120: 1 unresolved externals
Note, if you will, that both attempts to compile the code resulted in the same linker error, for the same symbol, in the same function. This is because the problem is actually caused by ~Foo(), not TriggerIssue(); the first declaration of TriggerIssue() merely exposed it, by forcing the compiler to generate code for ~Foo().
[Note that in my experience, Visual C++ will attempt to optimise a class out as much as is safely possible, and refuse to generate code for its inline member functions, if the class isn't actually used. This is why making TriggerIssue() an inline function prevented SomeFunc() from being called: Since TriggerIssue() wasn't called, the compiler was free to optimise it out entirely, which allowed it to optimise ~Foo() out entirely, including the call to SomeFunc().]
Test 3: External symbol provided.
Using the same A2.h, A1.h, and MyTest.cpp as in Test 2, I made a simple DLL that exports the symbol, then told the compiler to link with it:
// SomeLib.cpp
void __declspec(dllexport) SomeFunc() {}
Compile with:
cl SomeLib.cpp /LD
This creates SomeLib.dll and SomeLib.lib, along with some other files the compiler & linker use. You can then compile your example code with:
cl MyTest.cpp SomeLib.lib > MyTest.log
This results in an executable, and the following log:
MyTest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:MyTest.exe
MyTest.obj
SomeLib.lib
The solution:
To resolve this issue, you need to provide either the compiler or the linker with the library corresponding to the DLL SomeFunc() is imported from; if given to the compiler, it'll be passed directly to the linker. If SomeFunc() is contained in SomeFuncLib.dll, for example, you would compile with:
cl MyTest.cpp SomeFuncLib.lib
To illustrate the difference, I compiled the test code successfully twice (with slight modifications each time), and used dumpbin /symbols on the resulting object files.
dumpbin/symbols MyTest.obj > MyTest.txt
Example 1: Non-inline declaration commented out, TriggerIssue() not called.
This object file was generated by commenting out the first declaration of TriggerIssue() in your example code, but not modifying either A2.h or MyTest.cpp in any way. TriggerIssue() is inline, and not called.
If the function isn't called, and the compiler is allowed to inline it, then only the following will be generated:
COFF SYMBOL TABLE
000 00AB9D1B ABS notype Static | #comp.id
001 00000001 ABS notype Static | #feat.00
002 00000000 SECT1 notype Static | .drectve
Section length 2F, #relocs 0, #linenums 0, checksum 0
004 00000000 SECT2 notype Static | .debug$S
Section length 68, #relocs 0, #linenums 0, checksum 0
006 00000000 SECT3 notype Static | .text
Section length 7, #relocs 0, #linenums 0, checksum 96F779C9
008 00000000 SECT3 notype () External | _main
Note, if you will, that the only function symbol generated was for main() (which is implicitly extern "C" so it can link to the CRT).
Example 2: Result from Test 3 above.
This object file was generated as a result of successfully compiling Test 3 above. TriggerIssue() is inline, and called in main().
COFF SYMBOL TABLE
000 00AB9D1B ABS notype Static | #comp.id
001 00000001 ABS notype Static | #feat.00
002 00000000 SECT1 notype Static | .drectve
Section length 2F, #relocs 0, #linenums 0, checksum 0
004 00000000 SECT2 notype Static | .debug$S
Section length 68, #relocs 0, #linenums 0, checksum 0
006 00000000 SECT3 notype Static | .text
Section length C, #relocs 1, #linenums 0, checksum 226120D7
008 00000000 SECT3 notype () External | _main
009 00000000 SECT4 notype Static | .text
Section length 18, #relocs 2, #linenums 0, checksum 6CFCDEF, selection 2 (pick any)
00B 00000000 SECT4 notype () External | _TriggerIssue
00C 00000000 SECT5 notype Static | .text
Section length E, #relocs 0, #linenums 0, checksum 4DE4BFBE, selection 2 (pick any)
00E 00000000 SECT5 notype () External | ??0Foo##QAE#XZ (public: __thiscall Foo::Foo(void))
00F 00000000 SECT6 notype Static | .text
Section length 11, #relocs 1, #linenums 0, checksum DE24CF19, selection 2 (pick any)
011 00000000 SECT6 notype () External | ??1Foo##QAE#XZ (public: __thiscall Foo::~Foo(void))
012 00000000 UNDEF notype External | __imp_?SomeFunc##YAXXZ (__declspec(dllimport) void __cdecl SomeFunc(void))
By comparing these two symbol tables, we can see that when TriggerIssue() is inlined, the following four symbols will by generated if it is called, or omitted if it isn't:
_TriggerIssue (extern "C" void TriggerIssue())
??0Foo##QAE#XZ (public: __thiscall Foo::Foo(void))
??1Foo##QAE#XZ (public: __thiscall Foo::~Foo(void))
__imp_?SomeFunc##YAXXZ (__declspec(dllimport) void __cdecl SomeFunc(void))
If the symbol for SomeFunc() isn't generated, the linker doesn't need to link it, regardless of whether it was declared or not.
So, to summarise:
The problem is caused by ~Foo() calling SomeFunc(), when the linker doesn't have any SomeFunc() to link the call to.
The problem is exposed by TriggerIssue() creating an instance of Foo, and will show up either if TriggerIssue() is made non-inline (by the first declaration) or called when inline.
The problem is hidden if you comment out TriggerIssue()'s first declaraction and don't actually call it. Since you want the function to be inlined, and it isn't actually called, cl is free to optimise it out entirely. Optimising TriggerIssue() out also lets it optimise Foo's inline member functions out, which prevents ~Foo() from being generated. This, in turn, prevents the linker from complaining about the SomeFunc() call in the destructor, since the code to call SomeFunc() was never generated.
Or even shorter:
The first declaration of TriggerIssue() indirectly prevents the compiler from optimising out the call to SomeFunc(). If you comment out that declaration, the compiler is free to optimise TriggerIssue() and ~Foo() out entirely, which in turn stops the compiler from generating a call to SomeFunc(), allowing the linker to ignore it entirely.
To fix it, you need to provide a library that link can use to generate the proper code to import SomeFunc() from the appropriate DLL.
Edit: As user657267 pointed out in the comments, the specific part of TriggerIssue()'s first declaration that exposes the issue is the extern "C". Starting with the question's example program:
If the extern "C" is removed entirely from both declarations, and nothing else is changed, then the compiler will optimise TriggerIssue() (and by extension, ~Foo()) out as it compiles the code, generating a symbol table identical to the one in Example 1 above.
If the "C" is removed from both declarations but the function is left as extern, and nothing else is changed, then the linking stage will fail, producing the same log file as in Tests 1 & 2.
This suggests that the extern declaration is specifically responsible for preventing cl from optimising the problem code out, by forcing the compiler to generate a symbol that can be externally linked in other modules. If the compiler doesn't need to worry about external linkage, it will optimise TriggerIssue(), and by extension ~Foo(), out of the finished program entirely, thus removing the need to link to another module's SomeFunc().
SomeFunc is ODR-used in your program, so a definition must be available, but you haven't provided one (either in this translation unit or by linking in another) and your program has undefined behavior, no diagnostic required™.
The reason why the linker gives you an error is because the compiler has generated a definition for TriggerIssue; it's certainly curious that the behaviour is different depending on the presence of the extra declaration, you'd expect them to at least have the same behavior. UB aside, the compiler is still free to choose: the function is inline so you're guaranteeing that any and all definitions of the function will be identical, so if there are any dupe symbols at link time the linker can simply throw them out.

Link static member from dll

I've a strange problem with linking a static member from a dll using Visual Studio Express 2010. On Linux platform
and MSYS/MinGW (GCC) this failure doesn't occurs.
I've a Math library using an export macro explicitly for this library module:
#ifdef WIN32
#ifdef MATH_LIBRARY_EXPORT
#define MATH_LIBRARY_API __declspec(dllexport)
#else
#define MATH_LIBRARY_API __declspec(dllimport)
#endif
#else
//define empty values for linux OS
#define MATH_LIBRARY_API
#endif
And this is a snipped of my Vector class i export with static members:
ifndef BINREV_VECTOR_H__
#define BINREV_VECTOR_H__
// include common header with dll import/export macro
#include <brMath/brCommons.h>
namespace binrev{
namespace brMath{
class MATH_LIBRARY_API brVector3f
{
public:
float m_fX, m_fY, m_fZ;
brVector3f(void);
brVector3f(float x, float y, float z);
...
public:
static const brVector3f ZERO;
static const brVector3f NEGATIVE_UNIT_Z;
...
};
And the cpp module:
// Ensure that the dll hader will be exported
#define MATH_LIBRARY_EXPORT
#include <brMath/brVector3f.h>
namespace binrev{
namespace brMath{
const brVector3f brVector3f::ZERO(0.0f, 0.0f, 0.0f);
const brVector3f brVector3f::NEGATIVE_UNIT_Z( 0.0f, 0.0f, -1.0f);
...
In my Graphics module (is also an dll with an different explicit export macro) using this Math dll i try to access one of those static members:
#include <brMath/brVector3f.h>
brMath::brVector3f brCamera::getDirection(void)
{
return m_orientation.rotate(brMath::brVector3f::NEGATIVE_UNIT_Z);
}
On the other platforms anything works well, but with MVSE 2010 i got
a linker failure:
1>------ Erstellen gestartet: Projekt: ZERO_CHECK, Konfiguration: Debug Win32 ------
2>------ Erstellen gestartet: Projekt: brGraphics, Konfiguration: Debug Win32 ------
2> brCamera.cpp
2>brCamera.obj : error LNK2001: Nicht aufgelöstes externes Symbol ""public: static class binrev::brMath::brVector3f const binrev::brMath::brVector3f::NEGATIVE_UNIT_Z" (?NEGATIVE_UNIT_Z#brVector3f#brMath#binrev##2V123#B)".
2>C:\binrev\repository\binrevengine\modules\brGraphics\trunk\bin\brGraphics.dll : fatal error LNK1120: 1 nicht aufgelöste externe Verweise.
========== Erstellen: 1 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========
I'm not a friend of MVSE and this are my first tries to get our code runable with MVSE. While I've different solutions I've add the brMath.lib as additional dependency at the project settings. I've also set the path of the additional library directory to the location of my brMath.lib. This is the output of the linker command for review of my settings:
/OUT:"C:\binrev\repository\binrevengine\modules\brGraphics\trunk\bin\brGraphics.dll" /INCREMENTAL /NOLOGO
/LIBPATH:"C:\binrev\repository\binrevengine\modules\brMath\trunk\lib\Debug" /DLL "kernel32.lib" "user32.lib" "gdi32.lib"
"winspool.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "comdlg32.lib" "advapi32.lib" "brCore.lib" "brMath.lib"
"c:\binrev\development\vs2010\VC\lib\libboost_signals-vc100-mt-gd-1_47.lib" "c:\binrev \development\vs2010\VC\lib\libboost_system-vc100-mt-gd-1_47.lib"
/MANIFEST /ManifestFile:"brGraphics.dir\Debug\brGraphics.dll.intermediate.manifest" /ALLOWISOLATION
/MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"C:\binrev\repository \binrevengine\modules\brGraphics\trunk\bin\brGraphics.pdb"
/SUBSYSTEM:CONSOLE /STACK:"10000000" /PGD:"C:\binrev\repository\binrevengine\modules\brGraphics\trunk\bin\brGraphics.pgd" /TLBID:1
/DYNAMICBASE /NXCOMPAT /IMPLIB:"C:/binrev/repository/binrevengine/modules/brGraphics /trunk/lib/Debug/brGraphics.lib" /MACHINE:X86 /ERRORREPORT:QUEUE
It seems too me that anything is linked correctly. When I remove the call of the const static dll member of brVector3f the build is successfull. I think there must be a problem with the const static member. But why in hell only with MVSE ?
I couldn't find any difference to this introducion: http://stackoverflow...c-data-in-a-dll
So it normally should be working ...
Now i running out of ideas what could be wrong.
I'm grateful for any help or hint.
"...When modifying a variable or function at file scope, the static keyword specifies that the variable or function has internal linkage (its name is not visible from outside the file in which it is declared)." from MSDN