Инициализировать массив в конструкторе без использования конструктора или присваивания по умолчанию - PullRequest
4 голосов
/ 26 сентября 2010

Рассмотрим:

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 */
 {}
};

Я ожидал, что это сработает, поскольку я использую поддержку C ++ 0x в GCC4.3, который якобы поддерживает списки инициализаторов.Никакой радости.

У меня есть класс А, у которого нет конструктора по умолчанию.Это не подлежит обсуждению.Назначение после завершения по умолчанию не вариант.

Я пытаюсь создать B, который использует A. B :: foo не может быть std :: vector.

Как я могу инициализировать B::fooв B(...), создание его элементов ровно один раз?

В данный момент я готов заменить B на

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

Или даже использовать char foo [2*sizeof(A)] с размещением нового - YUK!

Конечно, есть правильный способ сделать это?

Ответы [ 4 ]

5 голосов
/ 26 сентября 2010

Вы можете использовать boost::array.Внутри него есть простой собственный массив, поэтому он по-прежнему имеет ту же структуру памяти, что и в вашем примере.

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

Если у вас нет буста, вы можете создать свой собственный класс array или использовать std::tr1, если у него есть его

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

Это все, что вам нужно, ноВы можете добавить обычные функции begin, end, size и т. д., чтобы сделать его более удобным для использования.

4 голосов
/ 26 сентября 2010

К сожалению, действительно нет правильного, чистого способа сделать это.Считайте это чем-то вроде языкового ограничения, которое возникает из-за неуклюжего смешения конструкторов C ++ и массивов стилей C.Стандарт C ++ 11 решает эту проблему, но до тех пор вам придется довольствоваться обходным решением.

Так как A не имеет конструктора по умолчанию, одним из возможных обходных путей является наличие массива A* указатели, а затем перебрать массив и инициализировать каждый из них с new.(Очевидно, не забудьте delete каждый элемент в массиве в деструкторе B, или просто используйте умные указатели.)

2 голосов
/ 26 сентября 2010

Должно работать в C ++ 0x, но g ++ 4.5.0 жалуется на «плохой инициализатор массива». Если вы замените A foo[2] на std::vector<A> foo, он скомпилируется.

1 голос
/ 26 сентября 2010

Ваш вопрос похож на предыдущий: C ++: инициализатор конструктора для массивов

Ваше основное предложение (сделайте двух членов foo_a и foo_b), вероятно, является наилучшим способом выполнения действий, при условии, что вам понадобятся только два элемента. Нет способа инициализировать элементы массива чем-либо, кроме конструктора по умолчанию для типа в списке инициализатора, как вы пытаетесь сделать в своем примере. Редактировать: Извините, я не видел, чтобы вы использовали C ++ 0x. В C ++ 0x вы должны иметь возможность инициализировать, как вы хотели, при условии, что A является копируемым или перемещаемым. Я не знаю о поддержке GCC 4.3 для этого, хотя.

Будьте осторожны при использовании char массивов и размещении новых - char массивы не обязательно выровнены должным образом для создания A, и это может привести к неопределенному поведению.

Некоторые другие предложения:

  • Сохраните массив указателей на A, или лучше, массив умных указателей, таких как auto_ptr или boost::scoped_ptr, и создайте объекты A в куче, используя new A(args...).
  • Сохраните массив другого типа, например boost::optional<A>, который обрабатывает конструкцию по умолчанию и проблему с выравниванием для вас, но по существу сохраняет объекты A внутри собственно объекта B.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...