Initialize array in constructor without using default constructor or assignment

13,407

Solution 1

Unfortunately, there really is no proper, clean way to do this. Consider it something of a language limitation that results from an awkward mixing of C++ constructors and C style arrays. The C++11 standard addresses this issue, but until then you'll have to settle for a workaround.

Since A has no default constructor, one possible work-around is to have an array of A* pointers, and then loop over the array and initialize each one with new. (Obviously, don't forget to delete each item in the array in B's destructor, or just use smart pointers.)

Solution 2

You can use boost::array. It has a plain native array inside, so it still has the same memory layout like in your example.

struct B {
 boost::array<A, 2> foo;

 B (const A & x, const A & y)
 : foo(createFoo(x, y))
 {}

private:
 static boost::array<A, 2> createFoo(const A & x, const A & y) {
   boost::array<A, 2> a = {{ x, y }};
   return a;
 }
};

If you don't have boost you can create your own array class or use std::tr1 if your compiler has it

template<typename T, std::size_t N>
struct array {
  T data[N];
};

That's all you need, but you can add the usual begin, end, size functions and so on to make it more comfortable to use.

Solution 3

It should work in C++0x, but g++ 4.5.0 complains with "bad array initializer". If you replace A foo[2] with std::vector<A> foo, it compiles.

Solution 4

Your question is similar to this previous question: C++: constructor initializer for arrays

Your main suggestion (make two members foo_a and foo_b) is probably the best way of doing things, provided that you'll need only two elements. There's no way of initializing array elements with anything but the type's default constructor in an initializer list, as you're trying to do in your example. Edit: Sorry, I didn't see that you were using C++0x. In C++0x you should be able to initialize as you wanted to do, provided that A is copyable or movable. I don't know about GCC 4.3's support for this, though.

Be careful using char arrays and placement new - char arrays aren't necessarily aligned properly to construct an A, and doing this can result in undefined behavior.

Some other suggestions:

  • Store an array of pointers to A, or better, an array of smart pointers like auto_ptr or boost::scoped_ptr, and create the A objects on the heap using new A(args...).
  • Store an array of another type, like boost::optional<A>, which handles the default construction and the alignment problem for you, but still essentially stores the A objects inside the B object proper.
Share:
13,407
spraff
Author by

spraff

Updated on June 04, 2022

Comments

  • spraff
    spraff about 2 years

    Consider:

    struct A {
    
     A (int);
    
     A (const A &);
    };
    
    struct B {
    
     A foo [2];
    
     B (const A & x, const A & y)
     : foo {x, y} /* HERE IS THE PROBLEM */
     {}
    };
    

    I was expecting this to work since I'm using C++0x support in GCC4.3, which allegedly supports initialiser lists. No joy.

    I have a class A which has no default constructor. This is not negotiable. Assignment post-default is not an option.

    I am trying to create B which uses A. B::foo may not be std::vector.

    How can I initialise B::foo in B(...), constructing its elements exactly once?

    At the moment, I am condidering replacing B with

    struct B {
    
    A foo_a;
    
    B foo_b;
    
    A * foo () {
    assert ((&foo_b) - *&foo_a) == 1);
    return &foo_a;
    }
    
    B (const A & x, const A & y) : foo_a(x), foo_b(y) {}
    };
    

    Or even using char foo [2*sizeof(A)] with placement new -- YUK!

    Surely there's a proper way to do this?

  • spraff
    spraff over 13 years
    Yes, that was the original intention. Any idea why this fails?
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    @spraff taking my comment back. Seems it only works for non-class element types even on GCC4.6 :(
  • fredoverflow
    fredoverflow over 13 years
    @spraff: Sounds like a g++ bug to me.
  • Charles Salvia
    Charles Salvia over 13 years
    +1 Nice solution. Although it will invoke A's copy constructor N times, unless the compiler has R-value constructor support.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    @Charles thanks. Though every copy involved except the unavoidable copies of x and y into a are eligible to be optimized: (1) copy of local a into the return value, and (2) copy of the return value temporary into foo.
  • sbi
    sbi over 13 years
    Well, if spraff has a compiler supporting C++0x, shouldn't that also come with std::array?
  • sbi
    sbi over 13 years
    Did you read that spraff wrote "I'm using C++0x support in GCC4.3, which allegedly supports initialiser lists"?
  • Charles Salvia
    Charles Salvia over 13 years
    I'm pretty sure GCC 4.3's C++0x extension doesn't support initializer lists. See gcc.gnu.org/projects/cxx0x.html