Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is declaration and definition defined this way in Effective C++?

Tags:

In Effective C++ (3rd Ed.), Item 2 (Prefer const, enum and inline to #define), the code segment for class-specific constants read:

class GamePlayer {
private:
    static const int NumTurns = 5;    // constant declaration
    int scores[NumTurns];             // use of constant
    ...
};

The book then says (in my own words) that static const int NumTurns = 5; is not a definition, which is normally required by C++ for class members unless it is a static integral constant whose address is never used. If the above is not true for the constant or if the compiler insists on a definition for any reason, the definition should be provided in the implementation file as follows:

const int GamePlayer::NumTurns;    // definition of NumTurns; see
                                   // below for why no value is given

According to the book (also in my own words), no value is given in the definition because it's already given in the declaration.

This is confusing what I thought I already know about the definitions of declaration and definition (and I double-checked on Google before asking this question):

  • Why is static const int NumTurns = 5 not a definition? Is NumTurns not initialized to a value of 5 here, and is it not that when a declaration and a definition occurs together, it is called an initialization?
  • Why do static integral constants not require a definition?
  • Why is the second code snippet considered to be a definition when no value is defined, yet the declaration inside the class which contains the value is still not one (essentially going back to my first question)?
  • Isn't initialization a definition? Why is the "only one definition" rule not violated here?

It's possible I'm just confusing myself here at this point, so can someone kindly re-educate me from scratch: Why are those two lines of code declarations and definitions and not the other, and are there any instances of initialization there? Is initialization also a definition?

Credit: Code snippets are directly quoted from the book.

Edit: With additional reference to What is the difference between a definition and a declaration?

  • A declaration introduces the identifier and type
  • A definition instantiates and implements

So, yeah...that doesn't seem to be what's going on here.

Edit 2: I consider it is possible that compilers may optimize a static integral constant by not storing it in-memory and just substituting it inline in the code. But if NumTurns address is used, why doesn't the declaration change into a declaration+definition automatically since the instantiation is already there?

Edit 3: (This edit has less to do with the original question, but is still outstanding from it. I place it here so that I don't need to copy-paste into the comments for each answer below. Please answer me for this in the comments. Thanks!)

Thanks for the answers. My head is much clearer now, but the last question from Edit 2 still remains: If the compiler detects conditions in the program where a definition is needed (eg. &NumTurns is used in the program), why doesn't it just automatically reinterpret static const int NumTurns = 5; as a declaration & definition instead of declaration-only? It has all the syntax a definition anywhere else in a program would have.

I come from a Java background in school, and have not needed to make separate definitions like these for static members in the manner above. I know C++ is different, but I want to know why the above is like this. A static integral member being substituted inline if the address is never used sounds more like an optimization to me rather than a fundamental feature, so why is it I need to work around it (providing a separate statement as the definition when the conditions aren't met even though the original statement's syntax is sufficient) rather than the other way round (compiler treating the original statement as a definition when it is needed to have one since syntax is sufficient)?

like image 860
thegreatjedi Avatar asked Jan 06 '16 09:01

thegreatjedi


People also ask

Why declaration is important in C programming?

Declarations are important because they inform the compiler or interpreter what the identifying word means, and how the identified thing should be used. A declaration may be optional or required, depending on the programming language.

What is definition and declaration in C?

i.e., declaration gives details about the properties of a variable. Whereas, Definition of a variable says where the variable gets stored. i.e., memory for the variable is allocated during the definition of the variable. In C language definition and declaration for a variable takes place at the same time.

What is declaration and definition in programming?

In computer programming, a declaration is a language construct specifying identifier properties: it declares a word's (identifier's) meaning. Declarations are most commonly used for functions, variables, constants, and classes, but can also be used for other entities such as enumerations and type definitions.

What is the difference between declaration and definition of a function in C?

The main difference between Function Declaration and Function Definition in C Programming is that Function declaration indicates what the function is and Function Definition indicates what the function does.


2 Answers

Disclaimer: I am not a standard guru.

I suggest you read the following two :

http://www.stroustrup.com/bs_faq2.html#in-class
and:
What is the difference between a definition and a declaration?

Why is static const int NumTurns = 5 not a definition? Is NumTurns not initialized to a value of 5 here, and is it not that when a declaration and a definition occurs together, it is called an initialization?

In high level, definition (as opposed to declaration) instantiates or implements the entity (variable, class, function).
In case of variables, definition makes the variable be allocated on the program memory.
In case of functions - definition gives instructions that can be compiled to assembly instructions.*

The line of code

static const int NumTurns = 5;

does not make NumTurns be allocated in the program memory by default, so it's only declaration. In order to make (the only) instance of NumTurns in the program memory you will have to provide the needed definition:

const int MyClass::NumTurns;  

Bjarne quote:

You can take the address of a static member if (and only if) it has an out-of-class definition:

class AE {
    // ...
public:
    static const int c6 = 7;
    static const int c7 = 31;
};

const int AE::c7;   // definition

int f()
{
    const int* p1 = &AE::c6;    // error: c6 not an lvalue
    const int* p2 = &AE::c7;    // ok
    // ...
}

Why do static integral constants not require a definition?

They do if you want to take their addresses.

Isn't initialization a definition?

No. initialization is the act of setting initial values in some entity (primitive,object).

void someFunc (){
  int x; //x is declared, but not initialized
  int y = 0; //y is declared+ initialized.
}

Why is the "only one definition" rule not violated here?

Why would it ? You still have one NumTurns in the entire code. The symbol MyClas::NumTurns appears only once. The definition only makes the variable appear on the program memory. ODR rule specify that any symbol may be declared only once.

EDIT: The question basically boils down to "Why can't the compiler decide on its own?" I am not a member of the comitee, so I can't give fully legit answer.
My smart guess is that letting the compiler decide things is not the C++ philosophy. when you let the compiler decide , e.g. where to declare a static integral const, I, as a developer, may raise the following issues:

1) what happens If my code is a headers-only library? where should my compiler declare the variable? in the first cpp file it encounters? in the file which containes main ?

2) if the compiler decides where to declare the variable , does I have any any guarantee that this variable will be declared alongside with other static variables (which I have manually declared), thus keeping the cache locality tight?

there are more and more questions raised as soon as we approve the mindset of "let the compielr go wild" I think this is a fundamental difference between Java and C++.

Java: let the JVM do its hueristics.
C++: let the developer do his profiling.

eventually, in C++ , the compiler checks that everything makes sence and turn the code into binary, not do the job instead of the developer.

*or bytecode, if you use managed C++ (boo...)

like image 67
David Haim Avatar answered Oct 14 '22 07:10

David Haim


One of the consequences of the one-definition rule is that a static member of a class can only have one definition. However, if multiple compilation units define it, there would be one definition for every compilation unit within a program. The net effect is that the declaration cannot be a definition without breaking the one-definition rule. In practice, linkers are not typically smart enough to resolve such multiple definitions.

The reason static integral constants do not require a definition is that it is not necessary. If the value is initialised within the class definition, the compiler can just substitute the initialised value whenever it is used. Practically, this means there is no need for that value to actually occupy a memory location in the program (as long as no code computes the address of that constant, in which case a definition would be needed).

Declaration, definition, initialisation are actually separate (albeit related) concepts. A declaration tells the compiler something exists. A definition is a type of declaration that causes that something to exist (so code with visibility of other declarations can refer to it) - for example, allocates memory for it. Initialisation is the act of giving a value. This distinction actually occurs in other parts of the language. For example;

#include <iostream>
int main()
{
     int x;   //  declaration and definition of x

     std::cout << x << '\n';    // undefined behaviour as x is uninitialised

     x = 42;   // since x is not yet initialised, this assignment has an effect of initialising it

     std::cout << x << '\n';    // OK as x is now initialised
}

In practice, an initialisation can be part of a declaration, but is not required to be.

Edited to respond to "Edit 3" in the original question:

C++ has a separate compilation model. Java's model relies on capability (smarter linker, run time linking) that C++'s model does not. In C++, if one compilation unit sees a declaration but no definition, the compiler simply assumes the definition is in another compilation unit. Typically (with a lot of build chains) the linker later detects if a necessary definition does not exist, so the linking stage fails. Conversely, if every compilation unit that needed a definition to exist actually created one, the compiler would break the "one definition rule", and a typical dumb linker - which among other things is not smart enough to collapse something defined repeatedly into a single definition - would complain about a multiply-defined symbol.

like image 22
Peter Avatar answered Oct 14 '22 09:10

Peter