30

I'm writing a template class and at one point in my code would like to be able to value-initialize an object of the parameterized type on the stack. Right now, I'm accomplishing this by writing something to this effect:

template <typename T> void MyClass<T>::doSomething() {
    T valueInitialized = T();
    /* ... */
}

This code works, but (unless the compiler is smart) it requires an unnecessary creation and destruction of the temporary T object. What I'd like to write is the following, which I know is incorrect:

template <typename T> void MyClass<T>::doSomething() {
    T valueInitialized(); // WRONG: This is a prototype!
    /* ... */
}

My question is whether there is a nice way to value-initialize the automatic object without having to explicitly construct a temporary object and assign it over to the automatic object. Can this be done? Or is T var = T(); as good as it gets?

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Good question. :) (Though you should know better than to thank in a question body!) – Lightness Races in Orbit Jun 09 '11 at 19:18
  • Out of curiosity, why is this a concern? If `T` is a user-defined type, it should have a default constructor. If it's an intrinsic type, the performance loss from copy-constructing should be negligible. Even that's assuming that the compiler won't optimize it away. – Maxpm Jun 09 '11 at 19:21
  • @Maxpm- This is mostly out of curiosity. I've always used the `T var = T();` syntax and figured that there was probably a cleaner way to do it. You're completely correct that the performance hit should be negligible. – templatetypedef Jun 09 '11 at 19:23
  • Also related: http://stackoverflow.com/questions/2671532/non-copyable-objects-and-value-initialization-g-vs-msvc – Ben Voigt Feb 19 '12 at 04:56

3 Answers3

24

The following uses copy-initialization, which is 'probably fine' 95% of the time in C++03:

T var = T();

But for generic (C++03) code, you should always prefer direct-initialization to account for that other 5%:

T var((T())); // extra parentheses avoid the most vexing parse – the extra parentheses
              // force the contents to be evaluated as an expression, thus implicitly
              // *not* as a declaration.

Or better yet, use the Boost.Utility.ValueInit library, which packages up the ideal behavior for you along with workarounds for various compiler deficiencies (sadly, more than one might think):

boost::value_initialized<T> var;

For C++11, one can use list-initialization syntax to achieve direct value-initialization in a significantly less noisy/ugly manner:

T var{}; // unambiguously value-initialization†

(N.b. technically this will invoke std::initializer_list<> constructors instead of performing value-initialization for certain pathological types. Presumably the net result should be the same.)

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • 8
    Ugh, that looks like LISP. `:)` Does this really work with all types? `+1` from me for suggesting a boost library. – sbi Jun 09 '11 at 19:21
  • Yes, it works with all types. The parentheses are benign but disambiguate the declaration. – ildjarn Jun 09 '11 at 19:25
  • Unless I'm misinterpreting the difference between direct- and copy-initialization, this only makes a difference in whether conversion constructors are permitted, not in whether a temporary object is created. Is this correct? If so, what advantage does this particular approach offer over the copy-initialized version? – templatetypedef Jun 09 '11 at 21:07
  • @templatetypedef : According to the standard there's no advantage either way in this context/usage, but some older compilers elide the copy in the case of direct-initialization and not in the case of copy-initialization (MSVC and Borland especially come to mind, but I seem to recall GCC prior to 3.4 being similarly affected). I.e., if your code is only going to be built with recent compilers it's inconsequential -- hence "*which is probably fine 99% of the time*." – ildjarn Jun 09 '11 at 21:19
  • 1
    This doesn't work with all types after all. Specifically, it doesn't work with non-copyable/non-moveable types. Happily, the Boost library does. – Ben Voigt Feb 19 '12 at 05:02
  • 1
    I didn't know the Most Vexing Parse surrendered if you threw enough parentheses at it. – Quentin Aug 07 '15 at 15:03
  • @Quentin : Indeed, the extra parentheses force the contents to be evaluated as an expression, thus implicitly _not_ as a declaration. This behavior has noticeable effects in other contexts, such as with `return` expressions (NRVO cannot apply, move is no longer implicit, etc.) and `decltype` semantics. – ildjarn Aug 07 '15 at 19:28
16

You can use curly braces in C++0x:

T valueInitialized{};
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
5

No, there isn't any other way to reliably value-initialize a template type in C++03.

If you can count on T only being class types with default constructors, you could just write

T valueInitialized;

but if T might as well be a built-in type,

T valueInitialized = T();

is the way to go.

Do you have any reason to not to trust your compiler to optimize away that copy?

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 1
    Oh, I absolutely trust the compiler to do it. :-) This is mostly a question about whether there's a more elegant way to do this that doesn't rely on a smart compiler author. – templatetypedef Jun 09 '11 at 19:21