1

The constructor semantics of int/double/etc. are:

int a; // uninitialized
int b = int(); // zero initialized
int c = int(4); // four

Is it possible to define a class with exactly the same behavior? I.e., one that has both uninitialized and initialized default constructors? I believe this is impossible, and currently work around it by making a constructor that compiles only when called with 0, but want to make sure there isn't a way to exactly mimic fundamental types.

Geoffrey Irving
  • 6,483
  • 4
  • 32
  • 40
  • 3
    I'm pretty sure there's not (unless you're talking about a POD that just aggregates a number of the above). – Jerry Coffin Apr 11 '12 at 21:00
  • `int b = int();` is actually _value-initialization_, which just happens to have the effect of zero-initialization for scalars. And if I'm guessing your motivation correctly, I would suggest you look at [Boost.Optional](http://www.boost.org/libs/optional/). – ildjarn Apr 11 '12 at 21:08
  • 1
    What is the rationale for wanting an uninitialized variable? If you explain your use case, there's probably a better way to approach it. – bta Apr 11 '12 at 21:08

8 Answers8

2

It is impossible, because as opposed to the built-in types, your class constructor will always be called.

If you really want to do "uninitialized" object (why ???? Don't you have enough bugs because of uninitialized variables?), you'll have to do tricks with placement new, probably.

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • It's amusing how often stackoverflow folk are shocked at perfectly reasonable questions. One of the design principles of C++ is that user defined types should be as identical as possible to builtin types, and this is one of the holes in the language. Also, in the related code, the only error caused at any point by an uninitialized variable would have been *much harder* to find with zero initialization, thanks to valgrind. – Geoffrey Irving Apr 11 '12 at 21:07
  • 1
    @Geoffrey : "*One of the design principles of C++ is that user defined types should be as identical as possible to builtin types*" No it isn't -- fundamental types, and scalars in particular, have special semantics all over the C++ standard, _especially_ in regards to initialization. – ildjarn Apr 11 '12 at 21:10
  • 1
    @GeoffreyIrving but one other principle of C++ is that the basic built-in types are **not** classes. If you `typedef` something to 'int', it will behave exactly the same as `int`. Why do you expect a non-class type and a class type to behave the same is beyond me. And I'm not amused by that at all. – littleadv Apr 11 '12 at 21:10
  • I don't expect it, and usually I don't want it. However, in this case the class in question is simply two __m128i's stuck together. A lot of the code switches back and forth between __m128i's and the doubled class, and the fact that the semantics are different is annoying in principle (before you ask, I'm targeting hardware that doesn't support AVX). – Geoffrey Irving Apr 11 '12 at 21:20
  • 1
    @Geoffrey : Assuming `__m128i` behaves as a scalar type, why not make your class an aggregate as Maxim's answer suggests? Such a type, when default-initialized, will remain uninitialized as you want. – ildjarn Apr 11 '12 at 21:23
  • @GeoffreyIrving what you want to do is possible, but not what you asked: *Is it possible to define a class with exactly the same behavior? I.e., one that has both uninitialized and initialized default constructors?*. In Max's answer - you don't have constructors at all. If you do have a constructor - my answer stands: it would be impossible. – littleadv Apr 11 '12 at 21:49
2

If no constructors defined:

struct A { int x; };

A a; // default-initialized (unintialized)
A b = A(); // value-initialized
A c = { 4 }; // four
ildjarn
  • 62,044
  • 9
  • 127
  • 211
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Yep, see @Jerry's comment, but its not a "classic" OOD class really. – littleadv Apr 11 '12 at 21:09
  • Hmm. I believed this doesn't work, but valgrind isn't telling me one way or the other. As far as I know, A() is still uninitialized. – Geoffrey Irving Apr 11 '12 at 21:24
  • If this actually does work I would love to know. Does anyone else know whether this is correct? My little test program prints zeros in both the "initialized" and "value-initialized" cases, but I think that both zeros are a coincidence. Valgrind doesn't tell me anything, presumably because the memory in question is stack reuse. – Geoffrey Irving Apr 11 '12 at 21:28
  • The x in class A only remains uninitialized because it is a built-in. All built-in instances in a class are uninitialized unless specifically initialized. However, for non-builtins, the default actor is still called. For example, the following will print BAR twice. – Jody Hagins Apr 11 '12 at 21:30
  • @GeoffreyIrving I tried it on GCC 4.4.3 and it works. Don't know who downvoted it. – littleadv Apr 11 '12 at 21:33
  • It only works because the instance variable in the class is a built-in -- which does not get initialized unless told to do so. See this example... struct Bar { Bar() { std::cout << "BAR\n"; } }; struct A { Bar bar; }; struct A { Bar bar; }; int main(int, char *[]) { A a; A b = A(); } – Jody Hagins Apr 11 '12 at 21:38
  • @GeoffreyIrving Yes, `A a;` here does default construction which is the same as no initialization in this case and `A b = A();` does value initialization which is the same as zero initialization in this case. See 8.5 [dcl.init] p6 and p7 in the C++ spec. – bames53 Apr 11 '12 at 21:39
  • @Geoffrey : Yes, this is guaranteed to work for C++03 and C++11 (but not C++98). See §8.5/5 in the standard. – ildjarn Apr 11 '12 at 21:39
  • @JodyHagins Bar is default initialized which calls the constructor for Bar. If you did `struct Bar { int x}; struct A { Bar bar; };` then `A a;` results in no initialization. – bames53 Apr 11 '12 at 21:42
  • 1
    @Geoffrey That's my point. Only built-in types will get this behavior. Any instance of the class that is not a built-in will still have its default constructor called. It only works if all instances are built-ins. – Jody Hagins Apr 11 '12 at 21:48
  • 1
    @Jody: The standard seems to say that the value-initialization concept propagates recursively to fields. – Geoffrey Irving Apr 11 '12 at 21:49
  • @JodyHagins It's not just built in types. You just have to ensure that your types are all trivially constructible, which is a recursive requirement. – bames53 Apr 11 '12 at 21:50
2

"uninitialized construction" is oxymoronic.

Lack of initialization in construction only occurs as a result of bugs (failing to recursively construct the entire object).

If a C++ class has a constructor, it is called. If for any reason it cannot be called, then that situation is erroneous (for instance wrong construction arguments passed that don't match any constructor).

A C++ class can contain members which are of basic types, and its constructors can neglect to initialize those members.

This could be exploited for optimization: if you have a complex initialization which happens later, you can avoid the overhead of writing to those members.

That would be the way to do it, rather than trying to create an entire class that can be left uninitialized.

If you really want that, a way to emulate it would be to create a POD (plain old datastructure) without any constructors, and then use some additional techniques so that it can be used in ways that guarantee its initialization. For instance make a derived class from it which adds construction, or make use of the curiously repeating template pattern, etc.

Then you can still use the POD directly if you want it not initialized.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • I already know how to make an uninitialized constructor: the question is whether you can have uninitialized and value initialized constructors coexisting. – Geoffrey Irving Apr 11 '12 at 21:38
1

The default contractor will always be called. There are lots of "tricks" you can do, but it depends on why you want this behavior, and what you want the user code to look like. You can define a special constructor that just doe not do anything with the data members and call that. Or, you can do lots of different things... one option is this...

You can also play with templates...

struct uninitialized_ctor {};

class Foo
{
public:
  int x;
  Foo() : x(42)
  {
    std::cout << "Foo::Foo\n";
  }
protected:
  Foo(uninitialized_ctor const &)
  {
    std::cout << "Foo::Foo - uninitialized\n";
  }
};


struct UninitializedFoo : public Foo
{
  UninitializedFoo()
    : Foo(uninitialized_ctor())
  {
    // Does not call base class ctor...
    std::cout << "UninitializedFoo\n";
  }
};


int main(int, char *[])
{
  Foo foo1;
  UninitializedFoo foo2;
  Foo * f1 = new Foo();
  Foo * f2 = new UninitializedFoo();

  std::cout << foo1.x << '\n' << foo2.x << '\n' << f1->x << '\n' << f2->x << '\n';
}

BUT -- it depends on what your real goal is... and how it impacts users of the code...

Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
0

It is impossible, default constructor will be called in any case.

D_E
  • 1,196
  • 11
  • 24
0

Aggregate class types have essentially the behaviour you like:

struct Foo { int a; int b; };

Foo x;   // x.a, x.b uninitialized

Foo * p = new Foo(); // p->x, p->y value-initialized, i.e. zero

Foo y { 1, 2 }; // brace-initialization does what you think

Foo z { };      // brace-initialization can also "fake" value-init for automatics

For aggregates, default- and value-initialization propagate recursively to members.

Of course you can also make a non-aggregate that leaves a member uninitialized:

struct Bar
{
    int a;
    int b;
    Bar() : a() { }  // no b!
    Bar(int a_, int b_) : a(a_), b(b_) { }
};

Bar x;   // x.a is value-, x.b is default-initialized
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
0

Perhaps you could do something like this:

template<typename T>
struct uninitialized {
    static_assert(std::is_trivially_destructible<T>::value,"T must have trivial dtor");

    alignas(T) char raw[sizeof(T)];

    template<typename... Us>
    T &initialize(Us &&... us) {
        return *(new (raw) T(std::forward<Us>(us)...));
    }

    T &get() { return reinterpret_cast<T&>(raw); }
};

trivial destructor is required because otherwise you'd have to keep track of whether the object had been constructed or not in order to destroy it appropriately.

bames53
  • 86,085
  • 15
  • 179
  • 244
-1
class A { 
public:
    A() {}

};
A* a = (A*)malloc(sizeof(A));

(kids, don't do this at home. Don't do this at all!)

nothrow
  • 15,882
  • 9
  • 57
  • 104