0
A.h

class A  
{  
   private:
     static const int b = 50;
     int c[b];
 };

 A.cpp

 #include "A.h"
 const int A::b;

 C.cpp

 #include "A.h"

The compiler issues me a warning saying b is defined multiple times and one is ignored. I need to define it in the class since i need to initialize the array. Alternatively I would need to use the enum approach to do it. But I wanted to know if this was possible?

James McNellis
  • 348,265
  • 75
  • 913
  • 977
ram
  • 63
  • 1
  • 4
  • Which compiler are you using? I tried this with g++ on Ubuntu (added a stub `main()` so the linker would run) and got no warnings at all. – QuantumMechanic Apr 09 '11 at 02:16

2 Answers2

6

I'm going to guess that you are using Visual C++, which has a rather horrible language extension, as described in "Microsoft Extensions to C and C++":

Out of Class Definition of static const Integral (or enum) Members

Under the standard (/Za), you need to make an out-of-class definition for data members. For example,

class CMyClass {
    static const int max = 5;
    int m_array[max];
}
...
const int CMyClass::max;   // out of class definition

Under /Ze, the out-of-class definition is optional for static, const integral, and const enum data members. Only integrals and enums that are static and const can have initializers inside a class; the initializing expression must be a const expression.

To avoid errors when an out-of-class definition is provided (when the out-of-class definition is provided in a header file and the header file is included in multiple source files), you should use selectany. For example:

__declspec(selectany) const int CMyClass::max = 5;

The /Ze flag is enabled by default. You have to explicitly use the /Za flag if you don't want the language extensions.

The code as written compiles and links as-is without error using g++ 4.5.2, Clang 3.0, and Visual C++ 2010 with the /Za flag set.

Removing the definition from the .cpp file resolves the issue if you want to compile with Visual C++, but then it won't work with other compilers (or with /Za) if you try to use the data member. For a portable workaround, you can use a conditional compilation block that checks to see whether the extensions are enabled:

#ifndef _MSC_EXTENSIONS
const int A::b;
#endif
Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 1
    "but then your code won't work with other compilers" This is incorrect. The code will compile and run just fine with gcc, (just verfied). Perhaps you could provide an example of a compiler where that line really is necessary? – Matt Phillips Apr 09 '11 at 02:38
  • @Matt: In _this specific case_, the definition is unnecessary because the static data member is not _used_ (in the One Definition Rule sense of the term). If the static data member was _used_ (e.g., if `&A::b` appeared somewhere in the program), you would assuredly get a linker error. If you don't intend to _use_ the data member, the preferred approach is to prevent _use_ by using an enum, e.g. `enum { b = 50 };`. – James McNellis Apr 09 '11 at 02:46
  • @Matt: You are correct, though, that it was quite unclear that I said "your code won't work." I didn't mean "the specific code you posted here won't work," I meant "in general, the code that you write won't work." I've clarified that sentence. Thanks for pointing that out. – James McNellis Apr 09 '11 at 02:50
  • in my test program, I had int d = A::b; qDebug() << d; and there was no problem (on gcc). – Matt Phillips Apr 09 '11 at 02:55
  • 1
    @Matt: Right, but if you had `const int* p = &A::b;`, you'd get a linker error. In your example, the lvalue-to-rvalue conversion takes place immediately, so the object isn't _used_ (_used_ is a fun word that has very specific meaning in C++; informally, it means "used like an object," i.e. anywhere that you need or might need its address). For more information, there was a bit of discussion about this subject in another question a couple of weeks ago: http://stackoverflow.com/questions/5446005 – James McNellis Apr 09 '11 at 03:01
0

You've given conflicting definitions. By giving the variable a value within the class definition, you're saying it's a compile-time constant that doesn't need any storage. Then you're trying to give it storage in the .cpp file.

Take it out of the .cpp and you'll be fine. Just don't try to take the address of it.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • And don't try to bind a reference to it, or [use it in certain forms of conditional expressions](http://stackoverflow.com/questions/5446005). Is there any benefit to using a non-defined integral-type static data member when you can just use an enum? – James McNellis Apr 09 '11 at 03:02