Eu preciso do modelo de classe de matriz C ++, que é de tamanho fixo, baseado em pilha e não requer construtor padrão

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

Pergunta

Então, eu tenho procurado o Boost :: Array, mas exige o construtor padrão definido. Eu acho que a melhor maneira de preencher essa matriz com dados seria através de um método push_back (const t &). Chamar mais vezes do que o tamanho (conhecido em tempo de compilação) resultaria em afirmação ou exceção, dependendo da configuração de construção. Dessa forma, ele sempre contém dados significativos. Alguém conhece implementação eficiente, portátil e confiável desse conceito?

Foi útil?

Solução

Bem, eu teria pensado que alguém teria trazido a resposta agora, mas não parece, então vamos lá.

O que você deseja é algo que eu sonhei: um boost::optional_array<T,N>.

Existem duas variantes:

  • Primeiro: semelhante a boost::array< boost::optional<T>, N >, esse é cada elemento pode ou não ser definido.
  • Segundo: semelhante a um std::vector<T> (de alguma forma), todos os elementos iniciais estão definidos e todos os seguintes não são.

Dadas as perguntas / comentários anteriores, parece que você gostaria do segundo, mas isso realmente não importa, pois ambos são iguais.

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

Vamos nos concentrar nessas operações muito especiais:

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

Como você pode notar, a principal dificuldade é lembrar que:

  • Se nenhum elemento foi construído lá ainda, você precisará de colocação nova + construção de cópia em vez de atribuição.
  • Elementos que se tornam "obsoletos" (ou seja, seria após o último elemento) deve ser descartado adequadamente (ou seja, seu destruidor seria invocado).

Existem muitas operações no contêiner STL tradicional que pode ser complicado de implementar. Com um vector, elemento embaralhando (devido a insert ou erase) são talvez os exemplos mais impressionantes.

Observe também que com C ++ 0x e listas inicializadoras vector pegue emplace_back para construir diretamente um elemento em vigor, levantando assim o CopyConstructible Requisito, pode ser um bom benefício dependente do seu caso.

Outras dicas

boost::array<T, 12> ta; não é diferente de T[12] ta;; Se você não usar uma lista de inicializador, os elementos serão construídos padrão.

A solução alternativa comum seria boost::array<T*, 12> ta; ou talvez boost::array<unique_ptr<T>, 12> ta;.

A única maneira de armazenar por valor é copiar, de maneira alguma ... é isso que as listas de inicializador fazem:

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

Isso sai:

A(int)
A(const A&)
A(int)
A(const A&)
~A()
~A()
~A()
~A()

http://codepad.org/vjgeqwk5

Pode ser armazenar um impulso :: variante em seu impulso :: Array? Faça do primeiro parâmetro int ou algo assim ..

ou seja

boost::array<boost::variant<int, foo>, 6> bar;

Ok, você tem que lidar com uma variante, mas é a pilha alocada ...

Em C ++ 0x você tem std::array<type, size> (Provavelmente o mesmo que Boost :: Array). Você pode inicializar dados da matriz usando fill() ou std::fill_n():

std::array<int, 30> array;
array.fill(0);
boost::array<int, 30> barray;
std::fill_n(barray.begin(), 30, 0);

Se você deseja que ele seja inializado na definição, você pode usar o copy-ctor:

static std::array<int, 30> const nullarray = {0, 0, 0, ..., 0}; // nullarray.fill(0);
// (...)
std::array<int, 30> array{nullarray};

Por que ele tem que residir na pilha? Você tem evidências empíricas de que criar e reservea vector é muito lento (usando um vector Parece a resposta óbvia)?

Mesmo que seja, você pode criar um conjunto de vetores que têm espaço reservado e swap um dos vetores pré-alocados em uma cópia local. Quando você terminar o local, troque de volta (muito parecido com o splice truque para lists).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top