6

I have run into a problem while writing C++ code that needs to compile in Visual Studio 2008 and in GCC 4.6 (and needs to also compile back to GCC 3.4): static const int class members.

Other questions have covered the rules required of static const int class members. In particular, the standard and GCC require that the variable have a definition in one and only one object file.

However, Visual Studio creates a LNK2005 error when compiling code (in Debug mode) that does include a definition in a .cpp file.

Some methods I am trying to decide between are:

  • Initialize it with a value in the .cpp file, not the header.
  • Use the preprocessor to remove the definition for MSVC.
  • Replace it with an enum.
  • Replace it with a macro.

The last two options are not appealing and I probably won't use either one. The first option is easy -- but I like having the value in the header.

What I am looking for in the answers is a good looking, best practice method to structure the code to make both GCC and MSVC happy at the same time. I am hoping for something wonderfully beautiful that I haven't thought of yet.

Community
  • 1
  • 1
Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • 3
    Visual studio shouldn't be doing that, I think you might be doing it wrong. – Seth Carnegie Aug 31 '11 at 21:18
  • Are you using `/Za` (disable language extensions). If not, is using this switch an option? http://connect.microsoft.com/VisualStudio/feedback/details/379496/static-const-member-variable-defined-multiple-times – CB Bailey Aug 31 '11 at 21:23
  • @Charles Bailey: I cannot use /Za. I just tried it, and all the Windows header files break horribly. I need those for things like the inline wrapped Windows/Pthread mutex functions so I cannot break *all* of the Windows code into OS specific .cpp files. – Zan Lynx Aug 31 '11 at 21:58
  • @Zan Is there a specific reason you "like having the value in the header"? – Branko Dimitrijevic Aug 31 '11 at 23:38
  • 1
    @Branko: Because it puts everything important about the class into one place. The header is like an outline of a class and the values are important for understanding how the class is used and what values are appropriate for array sizes, etc. – Zan Lynx Sep 01 '11 at 03:25
  • 2
    what's your reservation regarding enums? – Tobias Langner Sep 01 '11 at 06:56
  • @Zan But should it be a part of the class interface? After all, whatever value you choose to use may be considered an "implementation detail", which is legitimate to hide from the user. Also, I don't understand your "array sizes" argument, please clarify. Anyway, we can debate the aesthetics all we want, in the end of the day C++ has historical reasons why build process is the way it is, and the this in turn forces certain compromises of where things may and may not be placed in our code. – Branko Dimitrijevic Sep 01 '11 at 09:38
  • @Branko: I am not arguing with the C++ standard. I am looking for a good work around for the MSVC compiler which intentionally ignores the standard in this case. – Zan Lynx Sep 01 '11 at 20:29
  • @Branko: Array sizes: Class member functions in some cases take a pointer to a fixed-size array. The size of this array is set by a static const size_t class constant. – Zan Lynx Sep 01 '11 at 20:31
  • 1
    @Tobias: I'm not sure why I dislike enums. It seems like an abuse of their purpose. – Zan Lynx Sep 01 '11 at 20:33

4 Answers4

3

I generally prefer the enum way, because that guarantees that it will always be used as immediate value and not get any storage. It is recognized as constant expression by the compiler.

class Whatever {
    enum { // ANONYMOUS!!!
        value = 42;
    };
    ...
}

If you can't go that way, #ifdef away the definition in the .cpp for MSVC, because if you ifdef away the value in declaration, it will always get storage; the compiler does not know the value, so it can't inline it (well, the "link time code generation" should be able to fix that up if enabled) and can't use it where constant is needed like value template arguments or array sizes.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
2

If you don't dislike the idea of using non-standard hacks, for VC++ there's always __declspec(selectany). My understanding is that it will ensure that at link time, any conflicts are resolved by dropping all but one definition. You can potentially put this in an #ifdef _MSC_VER block.

asveikau
  • 39,039
  • 2
  • 53
  • 68
1

The Visual C++ 2010 accepts this:

// test.hpp:
struct test {
    static const int value;
};

// test.cpp:
#include "test.hpp"
const int test::value = 10;
Branko Dimitrijevic
  • 50,809
  • 10
  • 93
  • 167
  • Citing Zan "The first option is easy -- but I like having the value in the header." – osgx Aug 31 '11 at 23:24
  • Yep. And if no better answer shows up in a couple days I will accept this one. It seems like the best option available, sadly. – Zan Lynx Sep 01 '11 at 03:28
  • @Lynx: IMO it's the worst option, because it forces the value to be stored in the binary image and does not allow inlining it. – Jan Hudec Sep 01 '11 at 06:52
  • 1
    @Jan: your first part is correct, but the second part is not, it still gets inlined under MSVC (this is due to the LTO). GCC's LTO should be able to do the same (and strip the unused variables that got placed in the data section via `-fno-keep-static-consts`) – Necrolis Sep 01 '11 at 09:05
  • @Necrolis: Yes, LTO should be able to fix things up. But it's relying on optimizations, which may not happen from various subtle reasons, while providing the constant to compiler gives you other benefits here (it is constexpr then, so it can be used in places that need one). – Jan Hudec Sep 01 '11 at 09:37
0

This is still a problem with VS2013. I've worked around it by putting my standard-compliant definition, in the cpp file, inside a #if preventing VS.

a.h:

class A
{
public:
  static unsigned const a = 10;
};

a.cpp:

#ifndef _MSC_VER
unsigned const A::a;
#endif

I also commented it well, so the next guy in the file knows which compiler to blame.

John
  • 7,301
  • 2
  • 16
  • 23