C++: constructor initializer for arrays

C++ConstructorInitializer

C++ Problem Overview


I'm having a brain cramp... how do I initialize an array of objects properly in C++?

non-array example:

struct Foo { Foo(int x) { /* ... */  } };

struct Bar { 
     Foo foo;

     Bar() : foo(4) {}
};

array example:

struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     // ??? I know the following syntax is wrong, but what's correct?
     Baz() : foo[0](4), foo[1](5), foo[2](6) {}
};

edit: Wild & crazy workaround ideas are appreciated, but they won't help me in my case. I'm working on an embedded processor where std::vector and other STL constructs are not available, and the obvious workaround is to make a default constructor and have an explicit init() method that can be called after construction-time, so that I don't have to use initializers at all. (This is one of those cases where I've gotten spoiled by Java's final keyword + flexibility with constructors.)

C++ Solutions


Solution 1 - C++

There is no way. You need a default constructor for array members and it will be called, afterwards, you can do any initialization you want in the constructor.

Solution 2 - C++

Just to update this question for C++11, this is now both possible to do and very natural:

struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     Baz() : foo{{4}, {5}, {6}} { }
};

Those braces can also be elided for an even more concise:

struct Baz { 
     Foo foo[3];

     Baz() : foo{4, 5, 6} { }
};

Which can easily be extended to multi-dimensional arrays too:

struct Baz {
    Foo foo[3][2];

    Baz() : foo{1, 2, 3, 4, 5, 6} { }
};

Solution 3 - C++

Right now, you can't use the initializer list for array members. You're stuck doing it the hard way.

class Baz {
    Foo foo[3];

    Baz() {
        foo[0] = Foo(4);
        foo[1] = Foo(5);
        foo[2] = Foo(6);
    }
};

In C++0x you can write:

class Baz {
    Foo foo[3];

    Baz() : foo({4, 5, 6}) {}
};

Solution 4 - C++

Unfortunately there is no way to initialize array members till C++0x.

You could use a std::vector and push_back the Foo instances in the constructor body.

You could give Foo a default constructor (might be private and making Baz a friend).

You could use an array object that is copyable (boost or std::tr1) and initialize from a static array:

#include <boost/array.hpp>

struct Baz {

    boost::array<Foo, 3> foo;
    static boost::array<Foo, 3> initFoo;
    Baz() : foo(initFoo)
    {

    }
};

boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 };

Solution 5 - C++

You can use C++0x auto keyword together with template specialization on for example a function named boost::make_array() (similar to make_pair()). For the case of where N is either 1 or 2 arguments we can then write variant A as

namespace boost
{
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
    return boost::array<T,2> ({{ a }});
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
    return boost::array<T,2> ({{ a, b }});
}
}

and variant B as

namespace boost {
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
    boost::array<T,1> x;
    x[0] = a;
    return x;
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
    boost::array<T,2> x;
    x[0] = a;
    x[1] = b;
    return x;
}
}

GCC-4.6 with -std=gnu++0x and -O3 generates the exact same binary code for

auto x = boost::make_array(1,2);

using both A and B as it does for

boost::array<int, 2> x = {{1,2}};

For user defined types (UDT), though, variant B results in an extra copy constructor, which usually slow things down, and should therefore be avoided.

Note that boost::make_array errors when calling it with explicit char array literals as in the following case

auto x = boost::make_array("a","b");

I believe this is a good thing as const char* literals can be deceptive in their use.

Variadic templates, available in GCC since 4.5, can further be used reduce all template specialization boiler-plate code for each N into a single template definition of boost::make_array() defined as

/*! Construct Array from @p a, @p b. */
template <typename T, typename ... R>
boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b)
{
    return boost::array<T,1+sizeof...(R)>({{ a, b... }});
}

This works pretty much as we expect. The first argument determines boost::array template argument T and all other arguments gets converted into T. For some cases this may undesirable, but I'm not sure how if this is possible to specify using variadic templates.

Perhaps boost::make_array() should go into the Boost Libraries?

Solution 6 - C++

This seems to work, but I'm not convinced it's right:

#include <iostream>

struct Foo { int x; Foo(int x): x(x) { } };

struct Baz { 
     Foo foo[3];

    static int bar[3];
     // Hmm...
     Baz() : foo(bar) {}
};

int Baz::bar[3] = {4, 5, 6};

int main() {
    Baz z;
    std::cout << z.foo[1].x << "\n";
}

Output:

$ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit
g++ -pedantic    arrayinit.cpp   -o arrayinit
5

Caveat emptor.

Edit: nope, Comeau rejects it.

Another edit: This is kind of cheating, it just pushes the member-by-member array initialization to a different place. So it still requires Foo to have a default constructor, but if you don't have std::vector then you can implement for yourself the absolute bare minimum you need:

#include <iostream>

struct Foo { 
    int x; 
    Foo(int x): x(x) { }; 
    Foo(){}
};

// very stripped-down replacement for vector
struct Three { 
    Foo data[3]; 
    Three(int d0, int d1, int d2) {
        data[0] = d0;
        data[1] = d1;
        data[2] = d2;
    }
    Foo &operator[](int idx) { return data[idx]; }
    const Foo &operator[](int idx) const { return data[idx]; }
};

struct Baz { 
    Three foo;

    static Three bar;
    // construct foo using the copy ctor of Three with bar as parameter.
    Baz() : foo(bar) {}
    // or get rid of "bar" entirely and do this
    Baz(bool) : foo(4,5,6) {}
};

Three Baz::bar(4,5,6);

int main() {
    Baz z;
    std::cout << z.foo[1].x << "\n";
}

z.foo isn't actually an array, but it looks about as much like one as a vector does. Adding begin() and end() functions to Three is trivial.

Solution 7 - C++

Only the default constructor can be called when creating objects in an array.

Solution 8 - C++

In the specific case when the array is a data member of the class you can't initialize it in the current version of the language. There's no syntax for that. Either provide a default constructor for array elements or use std::vector.

A standalone array can be initialized with aggregate initializer

Foo foo[3] = { 4, 5, 6 };

but unfortunately there's no corresponding syntax for the constructor initializer list.

Solution 9 - C++

There is no array-construction syntax that ca be used in this context, at least not directly. You can accomplish what you're trying to accomplish by something along the lines of:

Bar::Bar()
{
	static const int inits [] = {4,5,6};
	static const size_t numInits = sizeof(inits)/sizeof(inits[0]);
	std::copy(&inits[0],&inits[numInits],foo);	// be careful that there are enough slots in foo
}

...but you'll need to give Foo a default constructor.

Solution 10 - C++

Ideas from a twisted mind :

class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
    //initialise with initVector[0] and then delete it :-)
}

};

now set this initVector to something u want to before u instantiate an object. Then your objects are initialized with your parameters.

Solution 11 - C++

You can do it, but it's not pretty:

#include <iostream>

class A {
	int mvalue;
public:
	A(int value) : mvalue(value) {}
	int value() { return mvalue; }
};

class B {
	// TODO: hack that respects alignment of A.. maybe C++14's alignof?
	char _hack[sizeof(A[3])];
	A* marr;
public:
	B() : marr(reinterpret_cast<A*>(_hack)) {
		new (&marr[0]) A(5);
		new (&marr[1]) A(6);
		new (&marr[2]) A(7);
	}

	A* arr() { return marr; }
};

int main(int argc, char** argv) {
	B b;
	A* arr = b.arr();
	std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n";
	return 0;
}

If you put this in your code, I hope you have a VERY good reason.

Solution 12 - C++

This is my solution for your reference:

struct Foo
{
    Foo(){}//used to make compiler happy!
    Foo(int x){/*...*/}
};

struct Bar
{
    Foo foo[3];
    
    Bar()
    {
        //initialize foo array here:
        for(int i=0;i<3;++i)
        {
            foo[i]=Foo(4+i);
        }
    }
};

Solution 13 - C++

in visual studio 2012 or above, you can do like this

struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     Baz() : foo() { }
};

Solution 14 - C++

class C
{
   static const int myARRAY[10];  // only declaration !!!

   public:
   C(){}
   }

const int C::myARRAY[10]={0,1,2,3,4,5,6,7,8,9};  // here is definition

int main(void)
{
   C myObj;
   }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJason SView Question on Stackoverflow
Solution 1 - C++AProgrammerView Answer on Stackoverflow
Solution 2 - C++BarryView Answer on Stackoverflow
Solution 3 - C++Michael KristofikView Answer on Stackoverflow
Solution 4 - C++UncleBensView Answer on Stackoverflow
Solution 5 - C++NordlöwView Answer on Stackoverflow
Solution 6 - C++Steve JessopView Answer on Stackoverflow
Solution 7 - C++Robert DemlView Answer on Stackoverflow
Solution 8 - C++AnTView Answer on Stackoverflow
Solution 9 - C++John DiblingView Answer on Stackoverflow
Solution 10 - C++the100rabhView Answer on Stackoverflow
Solution 11 - C++MoshevView Answer on Stackoverflow
Solution 12 - C++user7967189View Answer on Stackoverflow
Solution 13 - C++bowman hanView Answer on Stackoverflow
Solution 14 - C++jarjanView Answer on Stackoverflow