Eu preciso do modelo de classe de matriz C ++, que é de tamanho fixo, baseado em pilha e não requer construtor padrão
-
28-09-2019 - |
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?
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()
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 reserve
a 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 list
s).