I know how to initialize a static member that's not an integer, but I'm wondering, what is the rationale behind the syntax for this? I'd like to be able to just put the value in the class, like you can with an integer member, a la:
class A {
static const int i = 3;
};
I realise this could mean more rebuilding if I change the value since it's a change in the header - but in some cases that's pretty unlikely - and just as bad as changing a #define in the header anyway.
It doesn't seem like something that would be terribly hard for the compiler to understand. Are there technical reasons why it works the way it does? Or is it just a case of the compiler enforcing the good practice of separating the implementation from the definition?
Because that is the class declaration. You don't have any object yet.
You need to actually define the value somewhere --- somewhere specific.
Since it is static it's actually taking up space somewhere. But, since the .H file which has that declaration can be #included in many source files, which one defines holds the actual space it is using? Having the compiler automatically define the space in every object file and having the linker sort it out would be a violation of the "One Definition Rule".
A static class member has linkage, so it needs to be in a source file. Just because you declare it const doesn't mean it really can't change (please look up volatile for example).
This might help you:
class A {
enum { i = 3 }; // use an enum to set a constant value in the class declaration
void f() { int k = int(i); }
}
Related
C++17 introduced inline variable, and an inline static data member can be defined in the class definition with an initializer. It does not need an out-of-class definition. For example,
struct X {
inline static int n = 1;
};
Given this, I see no reason not to always use inline static data members, for the neat syntax. Any pitfall of doing this? Note that I don't mind slower compilation.
Not a pitfall, but here's one reason not to use an inline: if the initial value of the variable is not just a trivial constant, but something more complicated:
struct X {
inline static int n = and_then_more(figure_out_where_n_comes_from());
};
Now, the declaration of figure_out_where_n_comes_from() and and_then_more() must be pulled into the header file, now.
Also, whatever figure_out_where_n_comes_from() returns must also be declared. It could be some horribly overcomplicated class, which then gets passed to and_then_more(), as a parameter, to finally compute the initial value for n.
And everything that #includes the header file where X is declared must now include all the header files for all of these dependencies.
But without an inline, all you have is:
struct X {
static int n;
};
And you need to deal with all these dependencies only in one translation unit that instantiates X::x. Nothing else that #includes only X's header file cares about it.
In other words: information hiding. If it's necessary to reimplement where the initial value of n comes from, you get to recompile only one translation unit, instead of your entire source code.
For constants of a class, should I use class scope static const, or file scope const?
For example:
// .h
class C {
private:
static const int some_constant_c;
}
// .cc
const C::some_constant_c = 10;
vs.
// .h
class C {
}
// .cc
const some_constant_c = 10;
To me, the former one have better semantic meaning that the constant is of a certain class, but the latter one have the advantage not exposing constants to header file.
==============
One follow up question on this:
What if I want my constants be accessed by subclasses. Make sense to put static const in protected? Example follows:
// .h
class C {
protected:
static const int some_constant_c;
}
It's a matter of personal preference, of course. Trying not to expose class internals in the header file is a ship that has most definitely sailed in C++... between member variables and private member functions, it's just not practical to keep implementation details out of the header (unless you're using the pImpl idiom).
If all you want is to hide the value of the constant, note that you can put the initializer in the source file instead.
If you do implement the constants as globals in the source file, use an anonymous namespace to keep them from causing linker collisions.
I'd prefer 2nd variant, provided the const in the 1st case is private.
Why should one pollute the class declaration with redundant information?
Consider, you are implementing a protocol parser, with many many constants. How will the class declaration look like?
Another issue is, why should you type the name of the const twice? I try to keep definition and initialization as close as possible.
Just an opinion.
I've been learning C++, and I've come across static variable (I have prior knowledge from C89), and in the resource i'm using, they've declared a static variable in a class such as:
class nameHere
{
public:
static int totalNum;
}
int nameHere::totalNum = 0;
int main()
{}
For Example.
What I don't understand is that, since I've already declared that the static variable is an integer in the class definition, why do I need to also declare it as an integer outside of the class definition?
Would it not make sense to simply initialise it like so:
nameHere::totalNum = 0;
int main()
{}
Is there a particular reason or simply a convention of C++?
Thanks for all the help!
This would (probably) make the language even more difficult to parse (and it's already almost insanely difficult to parse anyway).
As it is, the datatype (int, long, my_class, whatever) tells the compiler that what it's seeing is the beginning of a declaration (which, in this case, is also a definition). Without that, the compiler would have a rather more difficult time sorting things out.
In the specific case of things at global scope, it wouldn't be that bad, because at global scope about all you can have is a series of declarations. At any other scope, however, things would be more difficult (and having one rule at global scope, and another elsewhere would be ugly indeed).
In C++11 you can simply initialize the variable inside the class:
class nameHere
{
public:
static const int totalNum = {0};
}
There is a difference between a definition and a declaration.
While the static variable in the class has been declared, it has not been defined. The One Definition Rule, explains declarations and definitions and states
In any translation unit, a template, type, function, or object can have no more than one definition. Some of these can have any number of declarations.
Therefore, the full type of the object must be used when declaring the variable.
My question: What exactly is the out of class definition of k doing under the hood to make sure it's address is available?
#include <iostream>
using namespace std;
class A {
public:
static const float k = 7.7;
};
//const float A::k; --> without this line compiler error
int main()
{
cout << &A::k;
}
The class "definition" is actually only providing a "declaration" of A::k. Yeah, I know it's confusing, but the idea is to allow the class definition to be in a .h (included from multiple .cpp sources) without creating ambiguities: one, and only one, of those .cpp sources, must provide the actual definition to match A::k's declaration (the latter being part of class A's definition).
Other answers have already given the how to fix it -- I'll go more into the why.
When you do this:
#include <iostream>
using namespace std;
class A {
public:
static const float k = 7.7;
};
int main()
{
cout << A::k;
}
The compiler is probably in reality generating this:
#include <iostream>
using namespace std;
class A {
public:
static const float k = 7.7;
};
int main()
{
cout << 7.7;
}
which means there will be no link-time dependency between this translation unit and A::f -- the compiled code doesn't reference A::f at all!
However, when you use &A::f, you force the compiler to generate an address for A::f. Therefore that translation unit does have a dependence on A::f. Since you have not defined it, you get a linker error, because the linker cannot find an address for it. For the address to exist, A::f must be defined in one and only one translation unit. To choose the translation unit in which it should reside, you need to define it there.
You also have an invalid code issue with your class above though -- only static const integral members may be initialized with the syntax you've used (putting k = 7.7 in the class body) -- float is not an integral type.
It sounds like you're wondering why the variable needs to be defined even though you aren't accessing it.
To print its address, it needs to have an address. To have an address, it must exist. To exist, it needs to have a definition and the linker needs to assign it a place in global variable space. So, there really isn't a middle ground.
"Under the hood," the definition tells the linker what the initializer for the global is. (In this case, the initializer is in the class block. But that's nonstandard. The official way is to write const float A::k = 7.7;) Without knowing that, it can't produce the executable file.
Also, unless the compiler performs impossibly detailed analysis, it can't really tell that operator << doesn't somehow pass that pointer to some other function or OS service that will access the value of k.
If you could define like static const float k = 7.7; as you wish, you will end up in multiple definitions (since static members will be defined only once), wherever you are including it.
To avoid that the definition is made reside separately in a cpp file.
From C++ standard docs sec 9.4.1,
A static data member is not part of the subobjects of a class. There is only one copy of a static data member shared
by all the objects of the class.
Also 9.4.2 states that,
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 scope enclosing the
member’s class definition.
Hope it helps..
The one which is inside the class is the declaration of the variable k. You need to define it in exactly one translation unit in order to link your program correctly. Hence that statement is required.
A static variable can be deemed as data shared by all the objects of the class and hence only one copy of this variable should be created. With this said, with whom should lie the responsibility of allocationg memory for this member? Obviously it can't be object's responsibility as there could be multiple objects which would raise another challenge as to which object should allocate memory for this member.
So typically compliler expects out of class, explicit definition of this member and hence the line:
const float A::k;
This ensures that the static member variable is accessible to all the objects of the class. The memory for this variable is allocated on globally accessible memory.
Is there any way, in C++, to define nested macros/constants within a class, in a similiar fashion to nested typedefs, or a method to acheive similiar functionality? The motive is generally for the macros to be used by templates.
class SomeClass
{
public:
#define SomeConstant 123
};
int x=SomeClass::SomeConstant;
Ofcourse, static const members can do the job, but those are physical variables, while I'm looking for a simple macro-like behavior.
You can't do what you want with macros. Macros have no concept of scoping.
But for simple int values you can do what you want with enums.
class SomeClass
{
public:
enum {
SomeConstant=123
};
};
int x=SomeClass::SomeConstant;
A fully scoped name for the value, but no space taken for it, even in debug builds - you couldn't take its address if you wanted to.
Const values declared and defined in-place are exactly what you need here: the compiler can and will optimise them away completely so they end up being exactly the same as using the #define in the first place, but with the benefits of strict scoping.
class SomeClass {
public:
static const int someValue = 10;
};
This doesn't take up any extra space - no memory is allocated to store "someValue". You can prove this: if you try and use the "someValue" as a real object (ie you try and get its address), then the linker will tell you it's undefined.
Macros completely ignore scope - they are expanded before the C++ compilation. You just can't do that.
Use of static const often leads to no variable being allocated - the compiler treats it as a constant.
Macro pre-processors generally don't have any idea of language context; thus they don't know what a "class" is making "nesting inside a class" not make sense in the first place.
For what you want, either use static const, or use the full name (assuming the preprocessor allows colons in macro names, not 100% sure on that) - though it won't allow you to inherit the constant on derived classes:
#define SomeClass::SomeConstant 123
You can do this with templates:
template < int someConstant = 123 > class SomeClass
{
public:
void outputConstant() { cout << "We think the answer is:" << someConstant; }
}
But that isn't precisely what you want because you have to declare an instance of the class as:
int main(int argc, char *argv)
{
SomeClass<123> myInstance;
}
I know others have explained the bit about macros, but allow me to add: #define is processed by the pre-processor, not the compiler. In the standard is a section called "translation phases" which explains this in more detail, but for your question the point is that macros are evaluated before the class is even compiled, and the scope at which the #define occurs is not known.
The authoritative book on this subject (programming with templates during the compile stage) is Modern C++ Design: Generic Programming and Design Patterns Applied, by Andrei Alexandrescu.
I don't understand your objection to using a static const.
It will not effect the size of your class, and the compiler will optimise to achieve what you think you'd get from a macro.
"Static members are physical variables".
What's against this? The only reason to object to this would be memory usage. But since the member is static, the memory would only be occupied once with the intended content.
On the contrary, with a macro, the contend would be present at every single usage location in the binary.
EDIT:
In case the variable is of an integral type, smaller than a pointer, it's probably best to define the constant in the class declaration. Optimizing compilers can then inline the value in the calling code, just like for a macro.