Предположим, вы хотите иметь статический массив предварительно определенных значений/объектов (const или non-const), связанных с классом. Возможные варианты использования std:vector
, std::array
или C-style array (ie. [])
, или. Например,
В .hpp:
class MyClass {
public:
static const std::vector<MyClass> vec_pre; // No efficient way to construct with initializer list, since it always uses Copy Contructor, even when using std::move
static const std::array<MyClass, 2> arr_pre; // Have to specify size which is inconvenient
static const MyClass carr_pre[]; // Not compatible with C++11 for-range since size is undefined
};
В .cpp
const std::vector<MyClass> MyClass::vec_pre = { std::move(MyClass{1,2,3}), std::move(MyClass{4,5,6}) }; // NOTE: This still uses copy constructor
const std::array<MyClass, 2> MyClass::arr_pre= { MyClass{1,2,3}, MyClass{4,5,6} };
const ZwSColour ZwSColour::carr_pre[] = { MyClass{1,2,3}, MyClass{1,2,3} }
При написании этого я выбрал std::vector
, так как мне не нужно указывать размер, я получаю всю доброту векторного класса, и это похоже на современный С++ способ сделать это. ПРОБЛЕМА: во время тестирования я заметил, что он вызовет конструктор Move, но затем все равно вызовет конструктор Copy для каждого элемента. Причиной этого является std::initializer_list
только разрешает доступ к const своим членам, поэтому вектор должен скопировать их из списка initializer_list в собственное хранилище. Несмотря на то, что это было сделано только один раз при запуске, , это неэффективно и, похоже, не существует способа, поэтому я просмотрел другие параметры (std::array
и C-array[]
).
Второй выбор заключался в использовании std::array
, который также является современным С++-способом, и он не страдает проблемой вызова Copy Constructor для каждого значения, поскольку ему не нужно создавать копии (не знаете, почему хотя точно?). std::array
также имеет то преимущество, что вам не нужно обертывать каждое значение в std::move()
. Тем не менее, у вас есть досада, что вы должны сначала указать размер, поэтому каждый раз, когда вы добавляете/удаляете элементы, вы также должны изменять размер. Есть способы обойти это, но ни один из них не идеален. Как утверждает @Ricky65, вы должны просто иметь возможность делать
std::array <int> arr = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 }; //automatically deduces its size from the initializer list :)
Это оставляет мне последний вариант - старый старый массив C-style [], который имеет те преимущества, которые мне не нужно указывать, и эффективен тем, что он не вызывает конструктор Copy для каждый объект. Недостатки в том, что это не очень современный С++, и самым большим недостатком является то, что если вы не укажете размер массива в заголовке .hpp, тогда С++ 11 для диапазона не будет работать, поскольку компилятор жалуется
Нельзя использовать неполный тип 'const MyClass []' как диапазон
Вы можете преодолеть эту ошибку, указав размер массива в заголовке (но это неудобно и создает сложный код, так как вам нужно настроить размер каждый раз, когда вы добавляете/удаляете элементы из инициализатора список), или альтернативно использовать constexpr
и полностью объявить массив и значения в заголовке .hpp
constexpr static MyArray my_array[] = { MyClass{1,2,3}, MyClass{4,5,6} };
ПРИМЕЧАНИЕ. Консольс "work-around" работает только для POD и поэтому не может использоваться в этом случае для объектов класса. Приведенный выше пример приведет к ошибке времени компиляции Invalid use of incomplete type 'MyClass'
Я пытаюсь писать лучшие на практике С++, где это возможно (например, используя идиому копирования и свопинга), и поэтому задайтесь вопросом, что является лучшим способом для определения статических массивов для класса...
- без указания размера
- который не обязательно должен быть скопирован (или Move сконструирован либо, если это возможно)
- который может использоваться с С++ для диапазона
- которые не обязательно должны указываться в файле заголовка
- Должен компилироваться/работать для Clang/LLVM 3.5, Visual Studio 2013 Update 4 RC и GCC 4.8.1.
EDIT2: Дополнительная информация об использовании std:: массива без указания размера, который также создает/использует make_array() и упоминает, что существует предложение для make_array(), чтобы стать стандартом. Оригинальная ссылка SO, предоставленная комментарием @Neil Kirk.
EDIT3: Еще одна проблема с методом vector
(по крайней мере в этом случае) заключается в том, что вы не можете перебирать элементы с помощью const T
или T
. Он допускает итерацию только с помощью const T&
(когда он static const
) и const T&
/T&
(когда он static
). Какова причина этого ограничения?
Описательный ответ на решения
@Yakk решение является единственным решением, а также работает на Visual С++ 2013 Update 4 RC.
Я нахожу это ошеломляющим, что такую тривиальную проблему сложно реализовать с использованием последнего стандарта С++ 11/14.