Итак, я смотрел на boost :: array, но для этого нужен конструктор по умолчанию. Я думаю, что лучший способ заполнить этот массив данными - использовать метод push_back (const T &). Вызов его больше раз, чем SIZE (известный во время компиляции), приведет к утверждению или исключению, в зависимости от конфигурации сборки. Таким образом, он всегда будет содержать значимые данные. Кто-нибудь знает эффективную, портативную, надежную реализацию этой концепции?
Мне нужен шаблон класса массива C ++, который имеет фиксированный размер, основан на стеке и не требует конструктора по умолчанию.
Ответы (5)
Что ж, я бы подумал, что кто-то принесет ответ сейчас, но, похоже, нет, так что поехали.
То, чего вы желаете, это то, о чем я мечтал: a boost::optional_array<T,N>
.
Есть два варианта:
- Первое: аналогично
boost::array< boost::optional<T>, N >
, то есть каждый элемент может быть установлен или не установлен. - Второе: похоже на
std::vector<T>
(как-то), то есть все начальные элементы установлены, а все последующие - нет.
Учитывая предыдущие вопросы / комментарии, кажется, вы хотели бы второй, но на самом деле это не имеет значения, поскольку оба они очень похожи.
template <typename T, size_t N>
class stack_vector
{
public:
bool empty() const { return mSize == 0; }
size_t size() const { return mSize; }
size_t capacity() const { return N; }
size_t max_size() const { return N; }
T& operator[](size_t i) { return *(this->pfront() + i); }
/// ...
private:
T* pfront() const { return reinterpret_cast<T*>(&mStorage); }
std::aligned_storage< N * sizeof(T), alignof(T) > mStorage;
size_t mSize; // indicate how many elements are set, from the beginning
};
Давайте сосредоточимся на этих очень специальных операциях:
template <typename T, size_t N>
void push_back(T const& t)
{
new (this->pfront() + mSize) T(t); // in place construction
++mSize;
}
template <typename T, size_t N>
void clear()
{
for (size_t i = 0; i != mSize; ++i)
{
(this->pfront() + i)->~T();
}
mSize = 0;
}
Как видите, основная трудность заключается в том, чтобы помнить следующее:
- если там еще не построен ни один элемент, вам нужно разместить конструкцию new + copy вместо присваивания.
- элементы, которые становятся «устаревшими» (то есть будут после последнего элемента), должны быть должным образом уничтожены (то есть их деструктор должен быть вызван).
В традиционном контейнере STL есть много операций, которые может быть сложно реализовать. На vector
перетасовка элементов (из-за insert
или erase
), возможно, является наиболее ярким примером.
Также обратите внимание, что с C ++ 0x и списками инициализаторов vector
get emplace_back
для непосредственного создания элемента на месте, таким образом отменяя требование CopyConstructible
, может быть хорошим благом в зависимости от вашего случая.
boost::array<T, 12> ta;
ничем не отличается от T[12] ta;
; если вы не используете список инициализаторов, элементы будут созданы по умолчанию.
Обычный обходной путь - boost::array<T*, 12> ta;
или, может быть, boost::array<unique_ptr<T>, 12> ta;
.
Единственный способ сохранить по значению - скопировать, никак иначе ... Вот что делают списки инициализаторов:
struct A {
A(int i):_i(i){ cout << "A(int)" << endl; }
A(const A& a){ cout << "A(const A&)" << endl; }
~A(){ cout << "~A()" << endl; }
int _i;
};
int main(){
boost::array<A, 2> ta = {{1, 2}};
}
Это выводит:
A(int)
A(const A&)
A(int)
A(const A&)
~A()
~A()
~A()
~A()
можно сохранить boost :: variant в вашем boost :: array? сделайте первый параметр int или что-то в этом роде ..
i.e.
boost::array<boost::variant<int, foo>, 6> bar;
хорошо, вы должны иметь дело с вариантом, но он выделен стеком ...
В C ++ 0x вы получили std::array<type, size>
(вероятно, то же самое, что и boost :: array). Вы можете инициализировать данные массива, используя fill()
или std::fill_n()
:
std::array<int, 30> array;
array.fill(0);
boost::array<int, 30> barray;
std::fill_n(barray.begin(), 30, 0);
Если вы хотите, чтобы он был инициализирован по умолчанию при определении, вы можете использовать copy-ctor:
static std::array<int, 30> const nullarray = {0, 0, 0, ..., 0}; // nullarray.fill(0);
// (...)
std::array<int, 30> array{nullarray};
int
вы используете определяемый пользователем тип, тогда все 30 будут построены по умолчанию (при использовании fill
), чего OP не хочет.
- person joshperry; 06.10.2010
doesn't require default constructor
...; f Итак, проблема не в контейнере - тогда OP должен удалить свой default-ctor или сделать его закрытым и, например, используйте sfinae для default-ctor-check
- person erjot; 06.10.2010
Почему он должен находиться в стеке? Есть ли у вас эмпирические доказательства того, что создание и reserve
создание vector
слишком медленно (использование vector
кажется очевидным ответом)?
Даже если это так, вы можете создать пул векторов с зарезервированным пространством и swap
один из заранее выделенных векторов в локальную копию. Когда вы закончите с локальным, снова поменяйте его местами (очень похоже на трюк splice
с list
s).
push_back
противоречит фиксированному размеру. Думаю, я знаю, что вы имеете в виду, что у него есть фиксированный верхний предел размера, и что вы не будете менять размер после того, как заполните его. Я не знаю об этом. - person Steve Jessop   schedule 06.10.2010char array[5]
имеет фиксированный размер, может быть шаблонизирован и основан на стеке. Бонус: работает даже вC
. : D - person ereOn   schedule 06.10.2010boost::array<T*>
тебя не устраивает? - person Matthieu M.   schedule 06.10.2010DefaultConstructible
наT
, что составляет суть вопроса. Не все объекты являютсяDefaultConstructible
, показательный пример: те, которые используют распределители вboost::interprocess
областях памяти. - person Matthieu M.   schedule 06.10.2010