Мне нужен шаблон класса массива C ++, который является фиксированным размером, на основе стека и не требует конструктора по умолчанию

StackOverflow https://stackoverflow.com/questions/3873625

Вопрос

Итак, я смотрю на Boost :: Array, но это требует определенного конструктора по умолчанию. Я думаю, что лучший способ заполнить этот массив с данными, был бы через метод Push_Back (Const T &). Называть его более раз, чем размер (известный по времени компиляции) приведет к утверждению или исключения, в зависимости от конфигурации сборки. Таким образом, он всегда будет содержать значимые данные. Кто-нибудь знает эффективное, портативное, надежное реализацию этой концепции?

Это было полезно?

Решение

Ну, я бы подумал, что кто-то принес ответ сейчас, однако кажется, не так давай.

Что вы желаете, это то, о чем я мечтал: 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;
}

Как вы можете заметить, основные трудности - помнить, что:

  • Если ни один элемент еще не был построен, вам нужно размещение нового + копировать конструкцию вместо назначения.
  • Элементы, которые становятся «устаревшими» (т. Е. Было бы после того, как последний элемент) должны быть правильно утилизированы (то есть вызываться их деструктора).

Существует много операций на традиционном контейнере STL, которое может быть сложно реализовать. На vector, элемент спреты (из-за insert или erase) Возможно, самые унылые примеры.

Также обратите внимание, что с C ++ 0x и списки инициализатора vector получать 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()

http://codepad.org/vjgeqwk5.

Может быть сохранить Boost :: Вариант в вашем Boost :: Array? Сделайте первый параметр Int или что-то в этом роде ..

т.е.

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};

Почему это должно проживать на стеке? У вас есть эмпирические доказательства, которые создают и reserveа. vector слишком медленно (используя vector Похоже, очевидный ответ)?

Даже если это, вы можете создать пул векторов, имеющих пространство, зарезервированную и swap один из предварительно выделенных векторов в локальную копию. Когда вы закончите с местным, снова поменяйте его обратно (очень нравится splice трюк lists).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top