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: Дикие и безумные идеи обходных путей приветствуются, но в моем случае они мне не помогут. Я работаю над встроенным процессором, где std::vector и другие конструкции STL недоступны, и очевидным обходным путем является создание конструктора по умолчанию и наличие явного метода init(), который можно вызывать после времени создания, так что я не вообще не нужно использовать инициализаторы. (Это один из тех случаев, когда меня испортило ключевое слово Java final + гибкость конструкторов.)
Ваши классы невозможно построить, потому что все private. John Dibling 09.03.2010
(ключевые слова доступа опущены для педагогической простоты) Jason S 09.03.2010
Не было бы проще использовать struct вместо class для педагогической простоты? Я нахожу код, который компилируется, легче учиться ;-) Steve Jessop 09.03.2010
Когда я скопировал ваш код в свой компилятор, мне пришлось добавить то, что вы пропустили. Так что для педагогической простоты вы можете подумать о том, чтобы не мешать людям помогать вам в будущем. John Dibling 09.03.2010
Стив/Джон: верно по обоим пунктам. моя вина. Jason S 09.03.2010
исправлено (я думаю... у меня нет быстрой и простой среды разработки C++ на моей машине) Jason S 09.03.2010
Выхода нет. Вам нужен конструктор по умолчанию для элементов массива, и он будет вызываться, после чего вы можете выполнить любую инициализацию в конструкторе.
Ответ 2 · рейтинг 40 · Barry
Просто чтобы обновить этот вопрос для С++ 11, теперь это можно сделать и очень естественно:
Вы можете использовать ключевое слово C++0xauto вместе с специализацией шаблона, например, для функции с именем 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 аргумент шаблона T, а все остальные аргументы преобразуются в T. В некоторых случаях это может быть нежелательно, но я не уверен, как это можно указать с помощью вариативных шаблонов.
Возможно, boost::make_array() следует перейти в библиотеки Boost?
Ответ 6 · рейтинг 2 · Steve Jessop
Кажется, это работает, но я не уверен, что это правильно:
Другое редактирование: это своего рода мошенничество, оно просто перемещает инициализацию массива для каждого члена в другое место. Таким образом, для 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 · рейтинг 1 · Robert
При создании объектов в массиве можно вызывать только конструктор по умолчанию.
Ответ 8 · рейтинг 1 · AnT
В конкретном случае, когда массив является элементом данных класса, вы не можете инициализировать его в текущей версии языка. Для этого нет синтаксиса. Либо предоставьте конструктор по умолчанию для элементов массива, либо используйте std::vector.
Автономный массив может быть инициализирован агрегатным инициализатором.
Foo foo[3] = { 4, 5, 6 };
но, к сожалению, нет соответствующего синтаксиса для списка инициализаторов конструктора.
Ответ 9 · John Dibling
Не существует синтаксиса построения массива, который можно было бы использовать в этом контексте, по крайней мере, напрямую. Вы можете выполнить то, что пытаетесь выполнить, с помощью чего-то вроде:
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 · the100rabh
Идеи извращенного ума:
class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
//initialise with initVector[0] and then delete it :-)
}
};
теперь установите этот initVector на то, что вы хотите, прежде чем создавать экземпляр объекта. Затем ваши объекты инициализируются с вашими параметрами.
Ответ 11 · Moshev
Можно, но некрасиво:
#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;
}
Если вы поместите это в свой код, я надеюсь, у вас есть ОЧЕНЬ веская причина.
Ответ 12 · user7967189
Это мое решение для справки:
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);
}
}
};