Могу ли я сохранить адрес, возвращенный std::initializer_list<T>::begin()?

У меня есть несколько статических экземпляров класса данных, которые содержат массивы целых чисел, упрощенные следующим образом:

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 является временным, поэтому держать адрес для этих данных неправильно.

Вопрос. Есть ли решение, позволяющее улучшить синтаксис без динамического выделения памяти? Выполнение копирования приводит к потреблению статической памяти и копированию данных, и в моем случае это нецелесообразно.

Это временно на время вызова конструктора. Вы должны скопировать содержимое в переменную-член (например, std::vector)

Pepijn Kramer 28.04.2023 19:37

@PepijnKramer - в моем случае использования я не хочу выделять кучу.

Steven 28.04.2023 19:41

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

NathanOliver 28.04.2023 19:42

Предпочитайте std::initializer_list<int> _ids, это позволяет компилятору лучше оптимизировать код с использованием регистров ЦП для значений — он пропускает начало и конец _ids через регистры ЦП и использует их напрямую, в противном случае код использует косвенные адреса _ids.

273K 28.04.2023 19:44

@ 273K Следующая ссылка относится к вашему удаленному ответу: timsong-cpp.github.io/cppwp/dcl.init.list#6

NathanOliver 28.04.2023 20:15
static ReadableIds xphase_one([]{ static int const data[]{ 1, 10, 3, 2, -1 }; return data; }());
Eljay 28.04.2023 20:16

Я не любитель макросов, но с помощью #define DATA(...) []{ static int const data[]{ __VA_ARGS__ }; return data; }() вы можете сделать его красивее.

Eljay 28.04.2023 20:22

@user17732522 user17732522 Да, мои извинения, я имел в виду [] и обновил вопрос, чтобы отразить это.

Steven 28.04.2023 20:23

@RaymondChen Когда я создаю объявление satic const int values[] = {1, 2, 3, 4, -1}, динамического распределения нет; это статическое распределение. Длина не требуется; компилятор может определить, сколько памяти требуется в сегменте данных программы. Я пытаюсь избежать попадания в кучу, что имеет место в моей первоначальной реализации. Просто это слишком многословно в моем реальном случае использования.

Steven 28.04.2023 20:33
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});
Raymond Chen 28.04.2023 20:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
10
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Нет, вы не можете сохранить результат _ids.begin(), точнее: вы можете сохранить его, но не можете разыменовать его после инициализации phase_two, где он становится оборванным указателем.

Ваши требования кажутся такими

  1. ReadableIds на самом деле не хранит сами элементы.
  2. Нет необходимости добавлять именованную переменную для хранения элементов.
  3. Также отсутствует динамическая продолжительность хранения элементов.

Тогда единственный способ - сохранить элементы во временном объекте массива. Проблема в том, что временные обычно уничтожаются после полного выражения, в котором они проявляются, а это не то, что вам нужно.

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.

Другие вопросы по теме