views:

238

answers:

5

I find it sometimes annoying that I have to initialise all POD-types manually. E.g.

struct A {
    int x;
    /* other stuff ... */

    A() : x(0) /*...*/ {}
    A(/*..*/) : x(0) /*...*/ {}
};

I don't like this for several reasons:

  • I have to redo this in every constructor.
  • The initial value is at a different place than the variable declaration.
  • Sometimes the only reason I have to implement a constructor is because of this.

To overcome this, I try to use my own types instead. I.e. instead of using int x,y;, I use my own vector struct which also initialize automatically with 0. I also thought about just implementing some simple wrapper types, like:

template<typename T>
struct Num {
    T num;
    Num() : num(0) {}
    operator T&() { return num; }
    operator const T&() const { return num; }
    T& operator=(T _n) { num = _n; return num; }
    /* and all the other operators ... */
};

This basically solves this so far for all cases where I want to init with 0 (that are by far the most often cases for me).

Thanks to James McNellis for the hint: This can also be solved via the boost::value_initialized.


Now, not limited to POD-types:

But sometimes I want to initialise with something different and there are the troubles again because that Num template struct cannot easily be extended to allow that. Basically because I cannot pass floating point numbers (e.g. float) as a template parameter.

In Java, I would just do:

class A {
    int x = 42;
    /*...*/

    public A() {}
    public A(/*...*/) { /*...*/ }
    public A(/*...*/) { /*...*/ }
    /*...*/
}

I find it quite important that in such cases where you want to init a member variable always in the same way in all possible constructors, that you are able to write the init value directly next to the member variable, like in int x = 42;.

So the thing I was trying to solve is to do the same thing in C++.

To overcome the problem that I cannot pass the init-value via a template parameter, I hacked together an ugly macro:

#define _LINENAME_CAT( name, line ) name##line
#define _LINENAME( name, line ) _LINENAME_CAT( name, line )

/* HACK: use _LINENAME, workaround for a buggy MSVC compiler (http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=360628)*/
#define PIVar(T, def) \
struct _LINENAME(__predef, __LINE__) { \
 typedef T type; \
 template<typename _T> \
 struct Data { \
  _T var; \
  Data() : var(def) {} \
 }; \
 Data<T> data; \
 T& operator=(const T& d) { return data.var = d; } \
 operator const T&() const { return data.var; } \
 operator T&() { return data.var; } \
}

(For other compilers, I can just omit that _LINENAME name for the struct and just leave it unnamed. But MSVC doesn't like that.)

This now works more or less like I want it. Now it would look like:

struct A {
    PIVar(int,42) x;
    /*...*/

    A() {}
    A(/*...*/) { /*...*/ }
    A(/*...*/) { /*...*/ }
    /*...*/
};

While it does what I want (mostly), I still am not fully happy with it:

  • I don't like the name PIVar (which stands for PreInitVar) but I really couldn't come up with something better. At the same time, I want to have it short.
  • I don't like that macro hack.

How have you solved this? Any better solution?


There was an answer which was deleted again which said that C++0x allows basically the same syntax as in Java. Is that true? So then I would just have to wait for C++0x.


Please don't give any comments like:

  • "then just use Java instead" / "don't use C++ then" or
  • "if you need something like this, you are probably doing something wrong" or
  • "just don't do it this way".

Also, please don't tell me not to use it. I know about all the drawbacks of my current solution. Please only make comments about non-obvious drawbacks if you are really sure that I am not aware of that. Please don't just state that there are many drawbacks in my current solution. Please also don't state that it is not worse to use it. I am just asking if you know about a better solution than the one I have presented here.

+9  A: 

Sometimes the only reason I have to implement a constructor is because of this.

You don't have to do that.

struct POD {
  int i;
  char ch;
};

POD uninitialized;

POD initialized = POD();

Equally in an initialization list:

class myclass
  POD pod_;
  // ....
  myclass()
   : pod_() // pod_'s members will be initialized
  {
  }

To overcome this, I try to use my own types instead.

Your type fails in this scenario:

void f(int&);

Num<int> i;
f(i);

There's likely more problems, but this is what occurred to me immediately.


How have you solved this? Any better solution?

Yes, we all have solved this. We did by not attempting to fight the language, but to use it the way it was created: initialize PODs in initialization lists. When I see this:

struct ML_LieroX : MapLoad {        
    std::string       id;
    PIVar(int, 0)     type;
    std::string       themeName;
    PIVar(int, 0)     numObj;
    PIVar(bool,false) isCTF;

I cringe. What is this doing? Why is it this way? Is this even C++?

All this just to save a few keystrokes typing an initialization list? Are you even serious?

Here's an old bon mot: A piece of code gets written once, but over its lifetime will be read tens, hundreds, or even thousands of times. That means that, in the long run, the time it takes to write a piece code is more or less neglectable. Even if it takes you ten times as long to write the proper constructors, but it saves me 10% of the time necessary to understand your code, then writing the constructors is what you should do.

sbi
No, the main point to have this is to have the initialization value directly next to the variable. This is something which makes the code more readable. In languages like Java where the language itself allows this, you are doing this also, instead of doing it in the constructor.
Albert
I extended my explanation in the question a bit. I hope it is more clear now why I think that my way is actually more readable (once you know what that `PIVar` does of course).
Albert
@Albert: Classes often have several constructors, and each might initialize a member variable to a different value. In languages like Java this isn't a big deal, because types are either primitives or _reference_ types, both of which are cheaply default-initialized just to be overwritten later. (Plus: No one write graphic card drivers in Java.) C++ follows a different pattern because its default is _value_ types. (Plus: People do write stuff like graphic card rivers in C++.)
sbi
Also, if I ever find myself getting tired of initializing countless data members in numerous constructors, I wonder what I've done wrong. My classes usually have not more than half a dozen data members. And if a class has more than four ways to be created, that's starting to be confusing to its users anyway. Often, that's a hint that your design isn't as good as it should be.
sbi
@Albert: "In languages like Java" Stop right there. This isn't Java, sorry. If you want to program Java, then put away the C++ compiler and program in Java. If you want to program in C++, you need to do things the C++ way. Wishing hard for C++ to be Java isn't useful. C++ doesn't have a way to be Java. You need to write constructors like a proper class typically should, write a named constructor (a function that returns an initialized object), leave them uninitialized and always do initialized later, or use those nasty macros and wear your flame-proof suit.
GMan
@GMan: No, I want to work in C++ and was searching for a similar solution. I used Java as an example because it shows exactly what construct I want to have. I already came up with a solution which works. I was just asking for a different one. Why exactly was it bad to use Java as an example to point out what I want?
Albert
@Albert: But it does _not_ work. I found a flaw the very moment I read your question.
sbi
@sbi: Sure it does. I don't actually use that `Num`, I use `PIVar`. And that works for your given example. But I will fix `Num` in my question so that also works... Edit: Now it will also work for your example.
Albert
@Albert: I'm afraid you're missing the point. Pointing at *any* language and saying "I want my language to behave that like language" is a bad approach. You use a language to solve problems, you don't use it to solve problems as if it were some other language. Embrace C++, don't try to get screwy with it because you don't feel like writing a constructor. Everyone that programs in C++ writes constructors, that's just how it goes. You should be asking "what's the best way to do this in C++?", not "this is the best way, how do I make it work in C++?".
GMan
@GMan: The question "what's the best way..." is a different one from the one I asked. And it probably would be closed as being subjective. And now, here where I asked about "how to do this", I don't want to know about that you may think this is bad. Btw., you have not explained why you think that. And do you know that this may even come with C++0x?
Albert
@Albert: `<sigh>` So? This still fails: `Num<int> i; int* r = ` And once you fix that, someone will quickly come up with something else that fails. A `Num<int>` isn't an `int` and there will always be some code that needs an object to be a `int` and which fails if it isn't.
sbi
@sbi: Yes I know. What is your point? I know that the solution is not perfect, that is why I was asking here at all. If it is true that the construct I was asking for in C++0x, than this may have solved my problem. Otherwise I am still searching for the best way. C++0x possibility to at least call other constructors also partly solves it (because I just have to write the init-code only once).
Albert
@GMan: Read [this](http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2756.htm). Do you really think that this is such a bad idea to have in C++?
Albert
@Albert: It's not a bad idea. There's a big difference between "let's write a macro hack to sort-of, kind-of make this good idea not quite work" and "let's propose that the language be modified to support this correctly." As for your attempt at the `Num<T>` template, make sure you read the Boost `value_initialized<T>` documentation that I linked in my answer; it discusses a number of caveats and issues that you need to take into account if you want to duplicate its functionality.
James McNellis
Yea, I know about those caveats. I also use this macro only rarely and only in cases where those caveats don't really matter. But again, that was the main point of my question: Is there a better way or not. And the answer seems to be: Not yet.
Albert
@Albert: No, I don't think it's a bad idea. But like @James said, that's a *language proposal*; that means you get the compiler to handle all the nasty details so you can focus on what the code *does*. Contrarily, you are not the compiler so you *do* have to care about those details, which unfortunately makes the code ugly and *hides* what the code does, and instead puts emphasis on how it does it. Trust me, I love syntactic sugar, but emulating it cleanly isn't a trivial task (look at Boost.Lambda!), and often times ends up just being worse. I think your case is such a case.
GMan
@GMan: Just curious: Why do you think that the syntax of `PIVar` is bad? Do you prefer templates instead of macros? So `PIVar<int,0>` would be better? Or don't you like the name? So `value_initialized<int>` is ok then? Or do you also think that `value_initialized` is also not worth it? For me, all of this looks pretty much like being in the scope of the language. I think when it comes to the question "is it worth it", it really becomes subjective. Also, all in all, I was not really asking about that. I mostly was interested in if there are others ways to automatically initialise something.
Albert
@Albert: For what it's worth, I spent considerable time looking into `value_initialized` and wrote several alternatives, but in the end decided that it wasn't worth the caveats and issues that it has. There were just too many things that were messy or didn't work cleanly enough to justify the benefits it provided.
James McNellis
+7  A: 

Boost provides a value_initialized<T> template that can be used to ensure an object (POD or not) is value-initialized. Its documentation goes into great detail explaining the pros and cons of using it.

Your complaint about not being able to automatically initialize an object to a given value doesn't make much sense; that has nothing to do with the object being POD; if you want to initialize a non-POD type with a non-default value, you have to specify the value when you initialize it.

James McNellis
Ah yes, of course it has nothing to do with that. I guess I was a bit confusing in my question about that. I maybe should have made 2 separate questions. `value_initialized` is the perfect answer to my first, thanks!
Albert
+2  A: 

You could initialize POD structures as follows:

struct POD
{
    int x;
    float y;
};

int main()
{
    POD a = {};            // initialized with zeroes
    POD b = { 1, 5.0f };   // x = 1, y = 5.0f

    return 0;
}
Kirill V. Lyadvinsky
A: 

There is a proposal for C++0x which allows this:

struct A {
    int x = 42;
};

That is exactly what I want.

If this proposal is not making it into the final version, the possibility of delegating constructors is another way of at least avoiding to recode the initialization in every single constructor (and at the same time avoiding a dummy helper function to do this).

In current C++, there does not seem to be any better way to do it despite what I have already demonstrated.

Albert
A: 

C++ does have constructor delegation, so why not use it?

struct AState
{
    int x;
    AState() : x(42) {}
};

class A : AState
{
    A() {}
    A(/*...*/) { /*...*/ }
    A(/*...*/) { /*...*/ }
};

Now initialization of x is delegated by all constructors. The base constructor can even accept arguments passed from each version of A::A.

Ben Voigt