tags:

views:

287

answers:

4

I've started to pick up this pattern:

template<typename T>
struct DefaultInitialize
{
   DefaultInitialize():m_value(T()){}
   // ... conversions, assignments, etc ....
};

So that when I have classes with primitive members, I can set them to be initialized to 0 on construction:

struct Class
{
  ...
  DefaultInitialize<double> m_double;
  ...
};

The reason I do this is to avoid having to remember to initialize the member in each constructor (if there are multiple constructors). I'm trying to figure out if:

  • This is a valid pattern?
  • I am using the right terminology?
A: 

If you're just initializing basic types to zero, you can override new and have it memset allocated memory to zero. May be simpler. There are pros and cons to doing this.

jeffamaphone
Please tell me you didn't mean to suggest `memset`ing over an entire class.
GMan
You've lost me.
cheez
He means that when ``new`` allocates memory from the heap, it zeroes out the acquired region before handing it over to the constructor (which then builds the vtable). Or more usefully, if you have rewritten new to use a pooled allocator, then you can zero out the entire pool when you first get it from the OS.
Crashworks
The main con being that new isn't called for automatics, so the constructor still needs to initialize the fields unless you ban placing your object on the stack. Of course if everything is done with factories, that might not be a difficult ban to enforce.
Steve Jessop
setting the raw memory to zero doesn't amount for type sensitive zeros (think of floating point zeros, or null pointers) that don't use all bits zero to represent their zero. His DefaultInitialize template will care for that, too.
Johannes Schaub - litb
True, but two-thirds of the way to the "doesn't work on non-2's complement" school of proper portability that nobody needs...
Steve Jessop
Note that I approve of proper portability, I just sometimes accept the YAGNI case against it.
Steve Jessop
IEEE754 specifies that floating point zero is in fact 0x00000000.
Crashworks
I'm really appalled that this is being downvoted, by the way. We actually do exactly this at my workplace and it saves us a world of typing and trouble.
Crashworks
I suggest everyone involved use their brains instead of assuming obviously erroneous things like overwriting vtable pointers. I said there are pros and cons to doing this; many projects find it useful, and others find it somewhat impure. I'm not advocating for or against it, merely pointing out that it is an option.
jeffamaphone
I agree that it's usually OK to use memset since most use ieee754 and most have zero null pointer. I wonder too who downvoted this. Overloading new this way certainly is an option. But i want to explicitly mention that data member pointers in the itanium ABI (used by GCC, too) have non-zero null pointer representations (they are stored as "-1" (i.e 0xffffffff on a two's complement))
Johannes Schaub - litb
Excellent example, litb, I'll remember that for future use. That's for null pointer-to-data-member, right? I wonder if that's why Itanium's dead ;-)
Steve Jessop
@onebyone, well it's used not only for itanium processors, but also for others and other compilers. That's why i would not use `memset` in this way. See this code: http://codepad.org/wEQEx4oI
Johannes Schaub - litb
+5  A: 

Seems like a lot of work to avoid having to type m_double(0). I think it's harder to understand at first glance, but it does seem fine as long as everything is implemented properly.

But is it worth it? Do you really want to have to #include "DefaultInitialize.h" everywhere?


To clarify, basically, you're:

  • Making your compile times longer because of the includes.
  • Your code base larger because you have to manage the deceptively simple DefaultInitialize class
  • Increase the time it takes other people to read your code. If you have a member of a class that's a double, that's natural to me, but when I see DefaultInitialize, I have to learn what that is and why it was created

All that because you don't like to type out a constructor. I understand that it seems very nice to not have to do this, but most worth-while classes I've ever written tend to need to have a constructor written anyway.

This is certainly only my opinion, but I think most other people will agree with it. That is: it would be handy to not have to explicitly initialize members to 0, but the alternative (your class) isn't worth it.

Not to mention that in C++0x, you can do this;

class Foo
{
private:
    int i = 0; // will be initialized to 0
}
GMan
That is a good point, but in practice I have not had a problem with it.
cheez
Perhaps you have not, others will.
Ed Swangren
Ed, can you elaborate?
cheez
Your point about compile times being longer is well taken. I've come to the conclusion that C++ is never going to not suck for compile times.Again, management of the DefaultInitialize class has not been a problem.I'd say it makes it more easier to read, especially when debugging. I really do not like to go hunting for initialization code.When I say "I don't want to type out a constructor" it's not because I'm lazy to type, but because it is easier to read (imo.)Did you have to think very hard about what the class does? Now when you see it will you ever be confused?
cheez
No, I understood the class easily, but the fact I did have to learn it is the point, here. I don't find this easier to read *at all*. If I want a `double` in my class, I want a `double`. Not some contorted version of a `double`.
GMan
I don't see a technical reason there. I can relate to your feelings though.I can guarantee you that out of 10 c++ programmers, a significant number of them could not tell me what happens if I did not have the DefaultInitialize<T> member.
cheez
I'm aiming for readability. Everyone should **always** aim for readable code. You're aiming for programmer convenience. It's easier to read POD types than POD types wrapped in a class. That's a fact. you and I both understand the class, yes, but that doesn't change the fact you are no longer dealing with a POD type; you are dealing with a POD type wrapped away. And if I understand your example, you're saying most of the ten C++ programmers wouldn't catch that the variables are being left uninitialized?
GMan
I don't think you read what I clarified above. I am aiming for readability, especially when it comes to debugging. For example, if I am debugging some ridiculous intermittent bug, it is easier for me to disqualify lack of initialization as the cause if I see this pattern. Typically I don't write POD classes because usually I have some member with a constructor anyway. And even if I make a POD into a non-POD, what's the big deal? As for the example, I'm saying a significant number (not most) of the C++ programmers would not catch it.
cheez
I disagree, then. Only poor programmers wouldn't catch that.
GMan
I don't think we disagree.
cheez
Then you think most programmers are bad?
GMan
In my experience, it's not that they are bad, it's that they don't have the proper knowledge. By that measurement, I would be poor on some days too :-)
cheez
Bad and lack of knowledge are synonymous, to me.
GMan
Then I guess we disagree after all :)
cheez
How could you disagree with that? One can't be a good tennis player without knowing how to use a racket. So how could someone be a good programmer without knowing the language? I'm baffled.
GMan
One doesn't need to know all of tennis to be a good tennis player. They just need to know how to beat their opponent. How else do you judge a good tennis player? Anyway, I hate analogies.
cheez
That's the point. ***You cannot be good at something if you don't *know* how to do it!*** They cannot beat their opponent if they can't hold a racket. Hating analogies doesn't dislodge them from being useful.
GMan
This is why I don't like analogies. One guy thinks his analogy is perfect, the other guy doesn't. Anyway, this is bordering on ridiculous, I'll just delete this question.
cheez
I don't think it's perfect. This is not subjective, analogies hold true as long as the properties being used in the analogies hold true. [http://en.wikipedia.org/wiki/Analogy] Read about logic sometime. My next comment with clarify for you. [http://en.wikipedia.org/wiki/Propositional_logic]
GMan
GMan
+1: This approach is classic over-engineering and you have introduced a dependency between 1 class and every other class that you write, which seems insane to me. Also, as GMan points out, after the new addition to C++0x has been rolled out, which allows initialisation in a far simpler way, you will be left with your entire code base being littered with a mass of redundancy.It's an awful lot to go through to avoid your programming team learning good programming habits.
Steg
And you just proved my point. "When" C++0x rolls out, I bet you I can upgrade old code to use the new feature faster than you.
cheez
I wouldn't have to upgrade my old code because it uses the current standard mechanism for initialising member variables - so that would take me 0 seconds.Anyway, the point I wanted to make is that I think that introducing a system that will proliferate through every single class in your code base for, what I see as, extremely little gain is not good practice. Feel free to take the final word, I wish you the best of luck.
Steg
Indeed, cheez. You seem to not want to learn. If you ask a question, are you just going to accept the answer that boosts your ego the most?
GMan
+5  A: 

This is a valid pattern?

It's a known "valid" pattern, i would say. Boost has a class template called value_initialized that does exactly that, too.

I am using the right terminology?

Well, your template can be optimized to have fewer requirements on the type parameter. As of now, your type T requires a copy constructor, unfortunately. Let's change the initializer to the following

DefaultInitialize():m_value(){}

Then, technically this kind of initialization is called value initialization, starting with C++03. It's a little bit weird, since no kind of value is provided in the first place. Well, this kind of initialization looks like default initialization, but is intended to fill things with zero, but respecting any user defined constructor and executing that instead.

To summarize, what you did was to value initialize an object having type T, then to copy that object to m_value. What my version of above does it to value initialize the member directly.

Johannes Schaub - litb
Oh, nice. Thanks litb for the link!
cheez
+2  A: 

Some compilers don't properly implement value initialization. For example, see Microsoft Connect, Value-initialization in new-expression, reported by Pavel Kuznetsov.

Fernando Cacciola's boost::value_initialized (mentioned already here by litb) offers a workaround to such compiler bugs.

Niels Dekker