I discovered this phenomenon today, where a member is unnecessarily constructed twice:
#include <iostream>
class Member {
public:
Member() {
std::cout << "Created member (default)" << std::endl;
}
Member(int i) {
std::cout << "Created member: " << i << std::endl;
}
};
class Object {
Member member;
public:
Object() {
member = 1;
}
};
int main() {
Object o;
return 0;
}
Is there a way to declare the member uninitialised - instead of using the default constructor - hence forcing you to use the initialiser lists in the constructor?
In Java, if you define a member like so: Member i; and you don't initialise it in every constructor, you'll get an error saying the field may be uninitialised, when trying to use it.
If I remove the default constructor from the Member class, I get the behaviour I want - the compiler forces you to use an initialiser list for every constructor - but I want this to happen in general, to stop me from forgetting to use this form instead (when a default constructor is available).
Essentially, I wanted protection against mistakenly using the default constructor, but it looks like this doesn't exist...
Even when marking the constructor with the explicit keyword, Member member still generates a member - that's immediately discarded when it's reassigned in the constructor. This itself seems inconsistent as well...
My main problem is the inconsistency. You can declare an uninitialised member if it has no default constructor; this is actually useful; you don't need to feed an initial redundant declaration, but simply initialise at the constructor (and break if not initialised). This functionality is completely missing for classes with a default constructor.
A related example is:
std::string s;
s = "foo";
You could simply do: std::string s = "foo"; instead, however if "foo" is actually multiple lines - as opposed to a single expression - we get non-atomic initialisation.
std::string s = "";
for (int i = 0; i < 10; i++) s += i;
This initialisation could easily end up in a torn write.
If you split it up, like so, it's assigned nearly atomically, however you still have the default value used as a placeholder:
std::string member;
// ...
std::string s = "";
for (int i = 0; i < 10; i++) s += i;
member = s;
In this code, you could actually simply move the member variable down after s is fully constructed; however, in a class, this isn't possible, as a member with a default constructor must be initialised at decleration - despite members without a default constructor not being restricted in the same way.
In the above case, the redundant use of std::string's default constructor is relatively inexpensive, but that wouldn't hold for all situations.
I don't want the default constructor gone, I just want an option to leave the member uninitialised until the constructor - the same way I can with types with no default constructor. To me, it seems like such a simple feature and I'm puzzled by why it's not supported/
It seems that this would have naturally been implemented (whenever uninitialised declaration of types with no default constructor was) if not for bracketless instantiation of a class being supported, which presumptuously instantiates classes - even when you want them left uninitialised, like my situation.
EDIT: Running into this problem again
In java you can do this
int x; // UNINITIALISED
if (condition){
x = 1; // init x;
}
else return;
use(x); // INITIALISED
In c++ this is not possible???
It initialises with the default constructor, but this isn't necessary - its wasteful.
- note: you can not use the uninitialised variable.
As you can see, because I'm using x outside of the loop, it has to get declared there, at which point it's - unnecessarily - initialised.
Another scenario where int x = delete would be useful. It would break no code, and only cause a compile-time error when trying to use the uninitialised x.
There's no uninitialised memory or undeterministic state, it's simply a compile-time thing - that Java has been able to implement well.