С++: инициализатор конструктора для массивов

У меня возникает судорога мозга... как я могу правильно инициализировать массив объектов на С++?

пример без массива:

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

struct Bar { 
     Foo foo;

     Bar() : foo(4) {}
};
Пример массива

:

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

struct Baz { 
     Foo foo[3];

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

изменить: Дикие и сумасшедшие обходные идеи оценены, но они не помогут мне в моем случае. Я работаю над встроенным процессором, где std::vector и другие конструкторы STL недоступны, и очевидным обходным решением является создание конструктора по умолчанию и наличие явного метода init(), который можно вызывать после времени построения, так что я не нужно использовать инициализаторы вообще. (Это один из тех случаев, когда я испортил Java final ключевое слово + гибкость с помощью конструкторов.)

Ответ 1

Нет никакого способа. Вам нужен конструктор по умолчанию для членов массива, и он будет вызываться, после чего вы можете выполнить любую инициализацию, которую вы хотите в конструкторе.

Ответ 2

Чтобы обновить этот вопрос для С++ 11, теперь это можно сделать и очень естественно:

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

struct Baz { 
     Foo foo[3];

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

Эти фигурные скобки также могут быть уменьшены еще более краткими:

struct Baz { 
     Foo foo[3];

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

Который может быть легко распространен и на многомерные массивы:

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

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

Ответ 3

В настоящий момент вы не можете использовать список инициализаций для элементов массива. Вы застряли, делая это тяжело.

class Baz {
    Foo foo[3];

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

В С++ 0x вы можете написать:

class Baz {
    Foo foo[3];

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

Ответ 4

К сожалению, нет возможности инициализировать элементы массива до С++ 0x.

Вы можете использовать std::vector и push_back экземпляры Foo в теле конструктора.

Вы можете дать Foo конструктор по умолчанию (может быть частным и сделать Baz другом).

Вы можете использовать объект массива, который является копируемым (boost или std:: tr1) и инициализируется из статического массива:

#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 };

Ответ 5

Вы можете использовать С++ 0x auto ключевое слово вместе с специализацией шаблона, например, с именем boost::make_array() (аналогично make_pair()). Для случая, когда N является 1 или 2 аргументами, мы можем тогда написать вариант A как

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 }});
}
}

и вариант B как

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 с -std=gnu++0x и -O3 генерирует тот же самый двоичный код для

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

используя как A, так и B, как и для

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

Для определяемых пользователем типов (UDT), однако, вариант B приводит к дополнительному конструктору копирования, который обычно замедляет работу, и поэтому его следует избегать.

Обратите внимание, что ошибки boost::make_array при вызове с явными литералами массива char, как в следующем случае

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

Я считаю, что это хорошо, поскольку литералы const char* могут быть обманчивыми при их использовании.

Шаблоны Variadic, доступные в GCC начиная с версии 4.5, могут быть использованы для уменьшения кода кодовой таблички специализации шаблона для каждого N в определении одного шаблона boost::make_array() определяется как

/*! 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... }});
}

Это работает в значительной степени, как мы ожидаем. Первый аргумент определяет аргумент boost::array template T, а все остальные аргументы преобразуются в T. В некоторых случаях это может быть нежелательным, но я не уверен, как это можно указать с помощью вариационных шаблонов.

Возможно, boost::make_array() должен войти в библиотеки Boost?

Ответ 6

Это похоже на работу, но я не уверен, что это правильно:

#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";
}

Вывод:

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

Предостережение emptor.

Изменить: нет, Комо отвергает его.

Другое редактирование: это своего рода обман, он просто толкает инициализацию массива по-члену в другое место. Поэтому для Foo требуется конструктор по умолчанию, но если у вас нет std::vector, вы можете реализовать для себя абсолютный минимум, который вам нужен:

#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 на самом деле не массив, но он выглядит примерно так же, как и вектор. Добавление функций begin() и end() в Three тривиально.

Ответ 7

При создании объектов в массиве можно вызывать только конструктор по умолчанию.

Ответ 8

В конкретном случае, когда массив является членом данных класса, вы не можете его инициализировать в текущей версии языка. Для этого нет синтаксиса. Либо укажите конструктор по умолчанию для элементов массива, либо используйте std::vector.

Отдельный массив может быть инициализирован с помощью инициализатора агрегата

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

но, к сожалению, нет соответствующего синтаксиса для списка инициализаторов конструктора.

Ответ 9

Нет синтаксиса построения массива, который можно использовать в этом контексте, по крайней мере, не напрямую. Вы можете выполнить то, что вы пытаетесь выполнить с помощью следующего:

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
}

... но вам нужно указать Foo конструктор по умолчанию.

Ответ 10

Идеи из искривленного ума:

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

};

теперь установите этот initVector на то, что вам нужно, до того, как вы создадите экземпляр объекта. Затем ваши объекты инициализируются вашими параметрами.

Ответ 11

Вы можете сделать это, но это не красиво:

#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 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;
}

Если вы поместите это в свой код, я надеюсь, у вас есть ОЧЕНЬ хорошая причина.

Ответ 12

Это мое решение для вашей справки:

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);
        }
    }
};

Ответ 13

в visual studio 2012 или выше, вы можете сделать это как

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

struct Baz { 
     Foo foo[3];

     Baz() : foo() { }
};

Ответ 14

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;
   }