У меня есть несколько статических экземпляров класса данных, которые содержат массивы целых чисел, упрощенные следующим образом:
class ReadableIds
{
public:
const int * ids;
ReadableIds( const int * _ids ) : ids(_ids) { }
// ... a bunch of methods that operate on ids, not germane to the discussion
};
В настоящее время я заполняю их следующим образом в filescope:
foo.cpp:
//
static const int phase_one_ids[] = { 1, 10, 3, 2, -1 };
static ReadableIds phase_one( phase_one_ids );
static const int phase_two_ids[] = { 31, 11, 23, 542, 11, 88, -1 };
static ReadableIds phase_two( phase_two_ids );
Тем не менее, я действительно хочу, чтобы они были определены встроенными по причинам удобочитаемости (класс сложнее с другими аргументами, переданными в конструктор)
static ReadableIds phase_one( { 1, 10, 3, 2, -1 }, ... other args );
static ReadableIds phase_two( { 31, 11, 23, 542, 11, 88, -1 }, ... other args );
Единственный способ скомпилировать это, если я введу std::initializer_list:
class ReadableIds
{
public:
ReadableIds(const std::initializer_list<int> & _ids ) :
ids(_ids.begin()) // take address of initializer_list !!
{
}
};
Но я понимаю, что std::initializer_list является временным, поэтому держать адрес для этих данных неправильно.
Вопрос. Есть ли решение, позволяющее улучшить синтаксис без динамического выделения памяти? Выполнение копирования приводит к потреблению статической памяти и копированию данных, и в моем случае это нецелесообразно.
@PepijnKramer - в моем случае использования я не хочу выделять кучу.
Список значений должен где-то жить. Вы либо используете массив, что означает возможную потерю пространства, если вы просто используете какой-то максимальный размер по умолчанию, либо ReadableIds должен стать шаблоном. В качестве альтернативы вы можете выполнить динамическое размещение, сохранив эти значения в vector
Предпочитайте std::initializer_list<int> _ids, это позволяет компилятору лучше оптимизировать код с использованием регистров ЦП для значений — он пропускает начало и конец _ids через регистры ЦП и использует их напрямую, в противном случае код использует косвенные адреса _ids.
@ 273K Следующая ссылка относится к вашему удаленному ответу: timsong-cpp.github.io/cppwp/dcl.init.list#6
static ReadableIds xphase_one([]{ static int const data[]{ 1, 10, 3, 2, -1 }; return data; }());Я не любитель макросов, но с помощью #define DATA(...) []{ static int const data[]{ __VA_ARGS__ }; return data; }() вы можете сделать его красивее.
@user17732522 user17732522 Да, мои извинения, я имел в виду [] и обновил вопрос, чтобы отразить это.
@RaymondChen Когда я создаю объявление satic const int values[] = {1, 2, 3, 4, -1}, динамического распределения нет; это статическое распределение. Длина не требуется; компилятор может определить, сколько памяти требуется в сегменте данных программы. Я пытаюсь избежать попадания в кучу, что имеет место в моей первоначальной реализации. Просто это слишком многословно в моем реальном случае использования.
template<std::size_t N> class ReadableIds { public: std::array<int, N> my_ids; ReadableIds(std::array<int, N> const& ids) : my_ids(ids) {} }; Теперь у вас есть копия в my_ids. auto x = ReadableIdx(std::array{1,2,3});




Нет, вы не можете сохранить результат _ids.begin(), точнее: вы можете сохранить его, но не можете разыменовать его после инициализации phase_two, где он становится оборванным указателем.
Ваши требования кажутся такими
ReadableIds на самом деле не хранит сами элементы.Тогда единственный способ - сохранить элементы во временном объекте массива. Проблема в том, что временные обычно уничтожаются после полного выражения, в котором они проявляются, а это не то, что вам нужно.
std::initializer_list конструкция может продлить время жизни до самого std::initializer_list объекта, но если вы не хотите называть переменную, вы ничего не сделали, кроме как перенесли проблему на продление времени жизни временного std::initializer_list объекта.
Единственный способ продлить время жизни временного объекта до времени жизни объекта класса — это сохранить ссылку (in) на временный объект непосредственно в классе и немедленно инициализировать его вновь проявленным временным объектом посредством инициализации агрегата в фигурных скобках.
Итак, вы можете сделать:
// DON'T USE THIS!
class ReadableIds
{
public:
// must be aggregate, no constructors!
std::initializer_list<int>&& _ids; // must be reference!
// other aggregate elements
};
Теперь вы можете использовать синтаксис
// DON'T USE THIS!
// must be braces!
static ReadableIds phase_one{ { 1, 10, 3, 2, -1 },
/*initializers for other aggregate elements*/ };
и массив, на который ссылается _ids, будет жить до тех пор, пока phase_one, но он не будет скопирован или его время жизни будет продлено, если phase_one будет скопировано/перемещено! Копия будет ссылаться на тот же массив и std::initializer_list объект! Копирование/перемещение std::initializer_list явно также не будет копировать/перемещать или продлевать время жизни базового массива!
В частности, если phase_one не был статическим объектом продолжительности хранения, то массив не будет жить до конца программы, а только до конца области, в которой объявлен phase_one.
Продление времени жизни также не работает, если вы используете круглые скобки вместо фигурных скобок для инициализации phase_one или если вы вставляете конструктор.
В общем, я бы не рекомендовал это. Это очень хрупко и не стоит того, чтобы не называть одну лишнюю переменную. (Я также не был бы уверен, что компиляторы правильно реализуют этот особый случай вложенного продления срока службы.)
Кроме того, пересмотрите вопрос о том, действительно ли необходимо первое требование. Достаточно просто сделать класс шаблоном, который автоматически определяет правильный размер внутреннего массива.
Немного более безопасным вариантом было бы использование массива вместо std::initializer_list. В этом случае вам нужно создать шаблон класса по размеру массива:
// DON'T USE THIS!
template<std::size_t N>
class ReadableIds
{
public:
// must be aggregate, no constructors!
int (&&_ids)[N]; // must be reference!
// other aggregate elements
};
Требования к инициализации по-прежнему применяются, но вероятность ошибочного копирования меньше _ids.
Это временно на время вызова конструктора. Вы должны скопировать содержимое в переменную-член (например, std::vector)