C ++ Постоянный анонимный экземпляр с агрегатной инициализацией - PullRequest
0 голосов
/ 25 октября 2019

По сути, я хочу получить указатель на константный и анонимный объект, такой как экземпляр класса, массива или структуры, который инициализирован с T {x, y, z...}. Извините за мои плохие навыки в формулировке.

Основной код, который я пытаюсь написать, выглядит следующим образом:

//Clunky, Im sure there is an inbuilt class that can replace this, any information would be a nice addition
template<class T> class TerminatedArray {
public:
  T* children;
  int length;

  TerminatedArray(const T* children) {
    this->children = children;
    length = 0;
    while ((unsigned long)&children[length] != 0)
      length++;
  }
  TerminatedArray() {
    length = 0;
    while ((unsigned long)&children[length] != 0)
      length++;
  }
  const T get(int i) {
    if (i < 0 || i >= length)
      return 0;
    return children[i];
  }
};

const TerminatedArray<const int> i = (const TerminatedArray<const int>){(const int[]){1,2,3,4,5,6,0}};

class Settings {
public:
  struct Option {
    const char* name;
  };
  struct Directory {
    const char* name;
    TerminatedArray<const int> const children;
  };

  const Directory* baseDir;
  const TerminatedArray<const Option>* options;

  Settings(const Directory* _baseDir, const TerminatedArray<const Option> *_options);
};


//in some init method's:
Settings s = Settings(
  &(const Settings::Directory){
    "Clock",
    (const TerminatedArray<const int>){(const int[]){1,2,0}}
  },
  &(const TerminatedArray<const Settings::Option>){(const Settings::Option[]){
    {"testFoo"},
    {"foofoo"},
    0
  }}
);

Код, на который я ссылаюсь, находится в самом низу, определениеs. Мне кажется, что я могу инициализировать константный массив целых чисел, но при применении той же методики к классам, она терпит неудачу с:
error: taking address of temporary [-fpermissive]

Я даже не знаю, поддерживает ли C ++ такие вещи,Я хочу избежать необходимости иметь отдельные определения const, загрязняющие и разделяющие код, и вместо этого иметь их чистыми и анонимными.

Причина, по которой все эти определения являются константами, заключается в том, что я работаю над проектом Arduino, который требуетэффективная балансировка SRAM для Flash. И в моем распоряжении много Flash.


У меня такой вопрос. Как я могу объявить постоянный анонимный класс / структуру, используя агрегатную инициализацию?

Ответы [ 3 ]

2 голосов
/ 25 октября 2019

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

Во-вторых, вы на самом деле не способны инициализировать постоянный массивцелые». Это просто компилируется. Попытка запустить его дает неопределенное поведение - в моем случае попытка построить i приводит к бесконечному поиску значения элемента 0.

В C ++ есть значения в стеке, есть значения в куче и естьвременные значения (я искренне извиняюсь перед любым, кто знает C ++ для этого утверждения).

  • Значения в куче имеют постоянные адреса, которые вы можете свободно передавать.
  • Значения в стеке имеютвременные адреса, которые действительны до конца блока.
  • Временные значения либо не имеют адресов (как предупреждает ваш компилятор), либо имеют действительный адрес на протяжении всего выражения, для которого они используются.

Вы используете такой временный объект для инициализации i и пытаетесь сохранить и использовать адрес временного объекта. Это ошибка, и для ее исправления вы можете создать свой «временный» массив в стеке, если вы не планируете использовать i вне блока, где будет находиться ваш массив.

Или вы можете создатьваш массив в куче, используйте его адрес для инициализации i и не забудьте явно удалить ваш массив, когда вы закончите с ним.

Я рекомендую прочитать https://isocpp.org/faq и ознакомиться с временем жизнипеременных и управления памятью, прежде чем пытаться исправить этот код. Это должно дать вам гораздо лучшее представление о том, что вам нужно сделать, чтобы ваш код делал то, что вы хотите.

Удачи.

2 голосов
/ 25 октября 2019

Прямой (и лучший) эквивалент TerminatedArray равен std::initializer_list:

class Settings {
public:
  struct Option {
    const char* name;
  };
  struct Directory {
    const char* name;
    std::initializer_list<const int> const children;
  };

  const Directory* baseDir;
  const std::initializer_list<const Option>* options;

  Settings(const Directory& _baseDir, const std::initializer_list<const Option>& _options);
};


//in some init method's:
Settings s = Settings(
  {
    "Clock",
    {1,2,0}
  },
  {
    {"testFoo"},
    {"foofoo"}
  }
);

https://godbolt.org/z/8t7j0f

Однако это почти навернякажизненные проблемы (о которых компилятор пытался предупредить вас с помощью «получения временного адреса»). Если вы хотите сохранить (не принадлежащий) указатель (или ссылку), тогда кто-то другой должен владеть объектом. Но при инициализации с такими временными объектами никто больше этого не делает. Временные значения умирают в конце полного выражения, поэтому ваши сохраненные указатели теперь указывают на мертвые объекты. Исправить это - другой вопрос (возможно, из-за того, что ваши требования противоречат друг другу).

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

1 голос
/ 25 октября 2019

&children[length] != 0 по-прежнему истинно или UB.

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

class Settings {
public:
  struct Option {
    const char* name;
  };
  struct Directory {
    const char* name;
    std::span<const int> const children;
  };

  const Directory baseDir;
  const std::span<const Option> options;

  Settings(Directory baseDir, span<const Option> options);
};

//in some method:
const std::array<int, 3> ints{{1,2,0}};
const std::array<Settings::Option> options{{"testFoo"}, {"foofoo"}};
Settings s{"Clock", {ints}}, options};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...