Reputation: 47945
Learning from this: By default, non-const variables declared outside of a block are assumed to be external. However, const variables declared outside of a block are assumed to be internal.
But if I write this inside MyTools.h
:
#ifndef _TOOLSIPLUG_
#define _TOOLSIPLUG_
typedef struct {
double LN20;
} S;
S tool;
#endif // !_TOOLSIPLUG_
and I include MyTools.h
3 times (from 3 different .cpp
) it says that tool
is already defined (at linker phase). Only if I change in:
extern S tool;
works. Isn't external default?
Upvotes: 2
Views: 1329
Reputation: 76315
There's a difference between a declaration and a definition. extern int i;
is a declaration: it says that there's a variable named i
whose type is int
, and extern
implies that it will be defined somewhere else. int i;
, on the other hand, is a definition of the variable i
. When you write a definition, it tells the compiler to create that variable. If you have definitions of the same variable in more than one source file, you've got multiple definitions, and the compiler (well, in practice, the linker) should complain. That's why putting the definition S tool;
in the header creates problems: each source file that #include's that header ends up defining tool
, and the compiler rightly complains.
The difference between a const
and a not-const
definition is, as you say, that const int i = 3;
defines a variable named i
that is local to the file being compiled. There's no problem having the same definition in more than one source file, because those guys all have internal linkage, that is, they aren't visible outside the source file. When you don't have the const
, for example, with int i = 3;
, that also defines a variable named i
, but it has external linkage, that it, it's visible outside the source file, and having the same definition in multiple files gives you that error. (technically, it's definitions of the same name that lead to problems; int i = 3;
and double i = 3.0;
in two different source files still are duplicate definitions).
Upvotes: 3
Reputation: 145279
The declaration S tool;
at namespace scope, does declare an extern
linkage variable. It also defines it. And that's the problem, since you do that in three different translation units: you're saying to the linker that you have accidentally named three different global variables, of the same type, the same.
One way to achieve the effect you seem to desire, a single global shared variable declared in the header, is to do this:
inline auto tool_instance()
-> S&
{
static S the_tool; // One single instance shared in all units.
return the_tool;
}
static S& tool = tool_instance();
The function-accessing-a-local-static is called a Meyers' singleton, after Scott Meyers.
In C++17 and later you can also just declare an inline
variable.
Note that global variables are considered Evil™. Quoting Wikipedia on that issue:
” The use of global variables makes software harder to read and understand. Since any code anywhere in the program can change the value of the variable at any time, understanding the use of the variable may entail understanding a large portion of the program. Global variables make separating code into reusable libraries more difficult. They can lead to problems of naming because a global variable defined in one file may conflict with the same name used for a global variable in another file (thus causing linking to fail). A local variable of the same name can shield the global variable from access, again leading to harder-to-understand code. The setting of a global variable can create side effects that are hard to locate and predict. The use of global variables makes it more difficult to isolate units of code for purposes of unit testing; thus they can directly contribute to lowering the quality of the code.
Disclaimer: code not touched by compiler's hands.
Upvotes: 5
Reputation: 17668
By default, non-const variables declared outside of a block are assumed to be external. However, const variables declared outside of a block are assumed to be internal.
That statement is still correct in your case.
In your MyTools.h
, tool
is external.
S tool; // define tool, external scope
In your cpp
file, however, the extern
keyword merely means that S
is defined else where.
extern S tool; // declare tool
Upvotes: 2