20

My question stems from studying Effective C++ by Scott Meyers. In Item II of that book, following is written :

To limit the scope of a constant to a class, you must make it a member and, to ensure there's at most one copy of the constant, you must make it a static member.

That is correctly written. Then immediately the following example is given :

class GamePlayer {
private:
    static const int NumTurns = 5;
    int scores[NumTurns];
    ....
  };

Then the following is written pertaining to the above example :

What you see above is a declaration and not a definition of NumTurns.

My First question is : What is the meaning of this statement ?

Immediately after that the following is mentioned :

Usually C++ requires that you provide a definition for anything you use, but class specific constants that are static and of integral type (e.g - integers, chars, bools) are an exception. As long as you don't take their address, you can declare them and use them without providing a definition. If you do take the address of a class constant, or if your compiler incorrectly insists on a definition even if you don't take the address, you provide a separate definition like this : const int GamePlayer::Numturns; //definition of NumTurns

Why now it is a definition and not a declaration ?

I understand the difference in the context of a function but do not understand it in the context of a regular variable. Also, can someone expand on what the author means by

... if you do take the address of a class constant, or if your .. part of the above quoted paragraph ?

P.S : I am a relatively newbie in C++.

0x499602D2
  • 87,005
  • 36
  • 149
  • 233
Ujjwal Aryan
  • 3,017
  • 3
  • 15
  • 28
  • 3
    Surely the book explains this ... Also a quick google search brings up this article which explains it pretty well: http://www.cprogramming.com/declare_vs_define.html – Fantastic Mr Fox Apr 29 '15 at 20:49
  • @Amxx yep, good point. This is a really well formatted question as well. +1. – Fantastic Mr Fox Apr 29 '15 at 20:53
  • This is related to the [odr rules](http://stackoverflow.com/q/8016780/1708801) in the case where a variable is not odr-used then it does not require an out of class definition. – Shafik Yaghmour Apr 29 '15 at 20:54
  • 1
    http://stackoverflow.com/questions/1410563/what-is-the-difference-between-a-definition-and-a-declaration – T.C. Apr 29 '15 at 20:54
  • I like to refer to static data members that are initialized in-class as "declarations with initializers". They are never definitions. – 0x499602D2 Apr 29 '15 at 21:06
  • My dog is black and he's a lab. That's a definition - it is not a dog. This is my dog, Spot. This is a declaration, it is an actual dog. It is a black lab. This is my dog, Fido. Another declaration, another black lab. – Mike Apr 29 '15 at 21:49

4 Answers4

8

Just as with functions, variables can have 'purely declarative' declarations, and actual definitions. You're confused because you probably didn't encounter many pure variable declarations before.

int i; // Definition
extern int i, j; // (Re)declares i, and declares j
extern int j = 0; // Defines j (confusing, eh?)

As you're used to with functions, definitions are declarations, but not all declarations are definitions. §3.1/2 reads

A declaration is a definition unless […] it declares a static data member in a class definition (9.2, 9.4),

Thus in-class static data member declarations are never defining the variables they declare. However, sometimes, a variables definition doesn't have to be present. That is the case when you can use its value directly without necessitating the variables run-time existence.

In technical terms, as long as a static data member (or any entity, for that matter) is not 'odr-used', it doesn't have to be defined. Odr-use for all entities is defined in §3.2/3:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).

This looks complicated, and it was simpler in earlier versions of the standard. However, it roughly says that the variable is not odr-used by some expression when that expression "immediately" accesses the variables value, and this access yields a constant expression. Meyers example of "taking its address" is only one of many for odr-use.

For some class A and its static data member i,

class A {
    static const int i = 57; // Declaration, not definition
};

const int A::i; // Definition in namespace scope. Not required per se.
Columbo
  • 57,033
  • 7
  • 145
  • 194
2

It is stated here that:

Static const integral data members initialized in the class interface are not addressable variables. They are mere symbolic names for their associated values. Since they are not variables, it is not possible to determine their addresses. Note that this is not a compilation problem, but a linking problem. The static const variable that is initialized in the class interface does not exist as an addressable entity.

What "addressable entity" refers here is "instance" of static const data type. No instance, no address, i.e its only a declaration.
Note that static variables that are explicitly defined in a source file can be linked correctly, though.

class X
{
    public:
        static int const s_x = 34;
        static int const s_y;
};

int const X::s_y = 12;

int main()
{
    int const *ip = &X::s_x;    // compiles, but fails to link
    ip = &X::s_y;               // compiles and links correctly
} 

... if you do take the address of a class constant, or if your .. part of the above quoted paragraph ?

It means if such a member is odr-used, a definition at namespace scope is still required, but it should not have an initializer.

struct X {
    const static int n = 1;
};
const int* p = &X::n; // X::n is odr-used
const int X::n;       // ... so a definition is necessary
haccks
  • 97,141
  • 23
  • 153
  • 244
  • 2
    That quote is incorrect. You can define a const integral data member and take its address. – Columbo Apr 29 '15 at 21:11
  • @Columbo; Its `static const`. – haccks Apr 29 '15 at 21:12
  • Sorry, I dropped the static. The comment is still valid. http://coliru.stacked-crooked.com/a/1b633f6939183e52 – Columbo Apr 29 '15 at 21:15
  • @Columbo; I am curious. Do you have any quote from standard? – haccks Apr 29 '15 at 21:18
  • 1
    @Columbo - No, you can't. You should not have downvoted this answer (I'm assuming you are the downvoter.) http://coliru.stacked-crooked.com/a/9c5df94daf8a700e – David Hammen Apr 30 '15 at 00:17
  • @DavidHammen Of course it won't link if you don't define it. The quote in this answer is marginal, though. After visiting the site, it seems that the author does cover definitions of these variables, and not including that is irritative. – Columbo Apr 30 '15 at 07:27
0

Why now it is a definition and not a declaration ?

Because this statement causes the compiler to generate an address for the static variable.

Also, can someone expand on what the author means by "if you do take the address of a class constant":

When you make a pointer point to a variable you take its address.

pjsofts
  • 142
  • 4
  • 18
0

A short answer is: a declaration says "this thing exists somewhere", and a definition causes the space to be allocated. In your case, you've declared it to be static and const. The compiler may be smart enough to notice that if you only ever use it as a value, it can simply replace that usage with the literal 5. It doesn't need to actually make space for a variable somewhere and fill it with the 5, it can use the 5 directly at compile-time. However, if you take the address of it, the compiler can no longer make that assumption and now needs to put that 5 into somewhere addressable. The compiler needs it to exist in one translation unit (roughly: one cpp file. See also the One Definition Rule.), so now you have to explicitly declare it somewhere.

Andre Kostur
  • 762
  • 1
  • 6
  • 15