Reputation: 7900
I am using GCC 4.8.2:
$ g++ --version
g++ (GCC) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
And I have this class definition:
#pragma once
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet(int inWidth, int inHeight);
Spreadsheet(const Spreadsheet& src);
~Spreadsheet();
int getId() const;
int getWidth() const;
int getHeight() const;
void setCellAt(int x, int y, const SpreadsheetCell& cell);
SpreadsheetCell getCellAt(int x, int y) const;
Spreadsheet& operator=(const Spreadsheet& rhs);
private:
bool inRange(int val, int upper) const;
void copyFrom(const Spreadsheet& src);
void freeMemory();
int mWidth, mHeight, mId;
SpreadsheetCell** mCells;
static int sCounter = 0;
};
When I try to compile it, I get:
$ make SpreadsheetTest && SpreadsheetTest
g++ -Wall -g -std=c++11 -c Spreadsheet.cpp
In file included from Spreadsheet.cpp:3:0:
Spreadsheet.h:27:31: error: ISO C++ forbids in-class initialization of non-const static member ‘Spreadsheet::sCounter’
static int sCounter = 0;
^
Makefile:11: recipe for target 'Spreadsheet.o' failed
make: *** [Spreadsheet.o] Error 1
The strange thing is that if I remove the static
modifier from sCounter
declaration, it compiles normally.
What is going on?
It seems like this feature is available since GCC 4.7: http://gcc.gnu.org/projects/cxx0x.html
I'm trying to find the official reference, but I took this piece of code from the book "Professional C++" - 2nd edition, from Gregoire, Solter & Kleper (Chapter 7, page 181, subsection "static Data Members") and it is supposed to work.
With C++11, that's all you need to do. If you are using C++ pior to C++11, it is a little bit clumsier [...]
And then it suggests me to do the traditional way.
Are the authors wrong?
Upvotes: 0
Views: 5571
Reputation: 320391
C++98 allows in-class initializers for static const
members of integral or enum types.
C++11 additionally allows in-class initializers for non-static members.
Your code does not fall into either category. It was never supposed to compile in C++98 and it is not supposed to compile in C++11. In C++ you are not allowed to provide in-class initializers for non-const static members.
Note also that the original C++98 feature has not been extended to non-integral non-enum types
struct S {
static const double d = 5; // still an error even in C++11
};
BTW, the semantics of and the rationale behind in-class initializers for static const and non-static class data members is completely different. Despite being similar syntactically, these are actually two different features, not an extension of the former onto the latter.
In-class initializers for static const data members exist to facilitate "early" Integral Constant Expressions, i.e. they turn a static constant onto an ICE at the point of class definition (as opposed to the later point of static member definition).
In-class initializers for non-static data member facilitate implicit initialization of class members in constructors. In other words, what in C++11 looks like in-class initializers for non-static data members do not really initialize anything yet. (There's nothing to initialize at that point.) All these in-class initializers do is supply the compiler with information that is used later for generating class constructors. These constructors will do they job even later, when you actually begin defining objects of that class type.
Upvotes: 1
Reputation: 55395
Read the error message again:
ISO C++ forbids in-class initialization of non-const static member ‘Spreadsheet::sCounter’ static int sCounter = 0;
If you want to initialize a static
member in class definition, it needs to be const
, that's all there is to it.
Normal class members are initialized when an object of that class is constructed. You can think of in-class initializers as a syntactic sugar for member initializer lists (or think of them as similar to default function arguments). You can still override it and initialize them with a different expression the old way (in a constructor).
Static members are different. They don't belong to any class instance and as such need to have memory allocated for them and be initialized separately. As they have static storage duration, that's somewhere at the programs startup.
Constant static members are an exception. As they can't be written to, the standard allows you to initialize them in class declaration and they're basically treated as a value, not an object. That is, until you do something with them that requires them to be stored somewhere, for example take their address. This hasn't changed in C++11.
Standardese quotes, from n3337 draft, chapter 9.4.2 (emphasis mine):
2 The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void. The definition for a static data member shall appear in a namespace cope enclosing the member’s class definition. In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the :: operator. The initializer expression in the definition of a static data member is in the scope of its class (3.3.7). [...]
and
3 If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment expression is a constant expression (5.19). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.
Upvotes: 3
Reputation: 56479
Move the static definition out of the class declaration:
class Spreadsheet
{
...
static int sCounter;
};
int Spreadsheet::sCounter = 0;
Upvotes: 1