On 12/16/2015 8:59 PM,
flimfl...@gmail.com wrote:
> I've long wished C++ worked a bit like this:
>
> int x; // initialized to zero in all scopes (not just global)
>
> int y = std::undefined; // it's left uninitialized, only when I say so
This is a good idea, and you're not the first to air it.
A simple but unfortunately verbose interim solution is to define a
wrapper like this:
struct Not_initialized {};
template< class Type >
struct Initialized_
{
Type value;
Initialized_(): value() {}
Initialized_( Not_initialized ) {} // !
};
and e.g. definining
using Int = Initialized_<int>;
and then you should be able to write
Int x; // Inialized to 0.
Int y{ Not_initialized }; // But the = syntax may cause actions.
cout << x.value*y.value << endl;
Of course the name "value" could be replaced with e.g. just "_" to cut
down on the verbosity.
A more extreme measure for more concise code is to implement a basic
type replacement class, i.e. a type with arithmetic operators,
comparison etc. The Boost library has some help for that. In principle
it could be as efficient as direct use of the build-in type it replaces.
> struct Foo {
> Foo() { }
> Foo(std::undefined_t) : x_(std::undefined) { }
> int x_;
> };
One way to guarantee initialization of members is to make them "const",
at the cost of losing copy assignment.
Another way is to use one of the two wrapper types sketched above.
> struct Bar {
> Bar() { }
> int x_;
> };
>
>
> Foo foo; // foo.x_ is zero
> Foo foo = std::undefined; // foo.x_ is undefined
Well yes, in C++03 it was a problem how to zero-initialize a POD struct
local automatic /variable/.
Doing it for dynamic allocation or for a temporary was easy, e.g.
Pod* p = new Pod(); // Value-initialized = zero for POD, hurray!
But trying that for a local variable, like
Pod f();
would just declare a function f; it's the infamous "most vexing parse".
For a variable one could write
Pod o = Pod();
but that was very ugly and verbose.
Happily with C++11 you can just write
Pod o{}; // Yay, braces to the rescue!
>
> Bar bar; // bar.x_ is zero
> Bar bar = std::undefined; // compiler error
>
> I know there's at least a few of us out there who would welcome this
> sort of thing. I also know there's massive opposition to this from
> really smart people, and so I want to learn from you. If I could
> understand why it's better for built-ins to be undefined by default
> rather than by request, it would be a great help to me.
Those who argue that the current rules are bestest are presumably silly
conformists.
The current rules were good for early C, where it was the programmer's
job to do micro-optimizations, but they're not good for modern C++.
Due to its history C++ has a lot of defaults backwards for modern
programming. For example, it would IMO be much better if objects were
"const" by default, and had to be declared mutable to be, well, mutable.
At one time I also argued that methods (non-static member functions)
should better have been virtual by default, but I'm not so sure now: I
find that my own default now is non-virtual, that for me, now, there
must be a good /reason/ for a method to be virtual.
> Please keep in mind I'm asking for guidance completely separate from
> the issues of legacy code or precedent set by C. I understand many of
> those issues. I instead want to learn why it shouldn't be done even if
> C++ were being designed for the first time today. If I could get that
> through my thick head, I'd be very happy.
I agree completely with you, IMO there isn't any technical reason why a
new language today should be like that.
I think those who argue otherwise (you indicate that such exist), do it
just out of conformity, a misplaced idea of belonging in a group of
followers of X by strongly denying any slightest blemish of X.
On the other hand, retrofitting the zero-initialization on C++, in a way
where one could not opt out of it, could break time-critical code in
e.g. real time systems.
> Here's something that may help understand part of where I'm hung up: >
> int x; // undefined
> float x; // undefined
> void* x; // undefined
> std::vector<int> x; // well defined
> std::string x; // well defined
> std::shared_ptr<int> // well defined (empty shared_ptr)
>
> In a perfect world, why should they be different?
In a perfect world one would have to explicitly accept the dangers of
the nano-optimization.
Btw., the term you're looking for isn't "undefined".
It's "indeterminate value".
> I've read rationale that cites valgrind. I get why valgrind is great
> for catching uninitialized data bugs, but I'm not sure why it would
> matter to valgrind whether the lack of initialization was default or
> explicit.
A debugger can rely on certain well know bitpatterns indicating most
likely uninitialized variables, e.g. CCCC... hex.
The idea is to fill memory with that bitpattern, and detect that it
apparently hasn't been changed in a variable (especially pointer).
But arguing that uninitialized variables are good because it allows a
debugger to detect uninitialized variables, is not very convincing...
Cheers!, & hth.,
- Alf
PS: Sorry if much of what I wrote here has already been discussed. I've
been offline for a while and there was just too much in this thread.