vector<int> v;
v.push_back(1);
v.push_back(v[0]);
Se o segundo push_back causar uma realocação, a referência ao primeiro número inteiro no vetor não será mais válida. Então isso não é seguro?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
Isso torna seguro?
push_back
. Outro pôster observou um bug , que não tratava adequadamente o caso que você descreve. Ninguém mais, pelo que sei, argumentou que isso não era um bug. Não estou dizendo que é uma prova conclusiva, apenas uma observação.Respostas:
Parece que http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 abordou esse problema (ou algo muito semelhante a ele) como um potencial defeito no padrão:
A resolução proposta era que este não era um defeito:
fonte
v.insert(v.begin(), v[2]);
não pode disparar uma realocação. Então, como isso responde à pergunta?Sim, é seguro e as implementações de bibliotecas padrão saltam através de argolas para fazê-lo.
Acredito que os implementadores rastreiam esse requisito de volta a 23.2 / 11 de alguma forma, mas não consigo descobrir como e também não consigo encontrar algo mais concreto. O melhor que posso encontrar é este artigo:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
A inspeção das implementações do libc ++ e libstdc ++ mostra que elas também são seguras.
fonte
vec.insert(vec.end(), vec.begin(), vec.end());
?vector.push_back
caso contrário, especifica. "Causa a realocação se o novo tamanho for maior que a capacidade antiga." e (atreserve
) "A realocação invalida todas as referências, ponteiros e iteradores que se referem aos elementos na sequência."O padrão garante que mesmo o seu primeiro exemplo seja seguro. Citando C ++ 11
[sequence.reqmts]
Portanto, mesmo que não seja exatamente trivial, a implementação deve garantir que não invalidará a referência ao fazer o
push_back
.fonte
t
, a única questão é se antes ou depois de fazer a cópia. Sua última frase está certamente errada.t
atenda às pré-condições listadas, o comportamento descrito é garantido. Uma implementação não tem permissão para invalidar uma pré-condição e, em seguida, use isso como uma desculpa para não se comportar conforme especificado.for_each
seja necessário para não invalidar os iteradores. Não consigo criar uma referência parafor_each
, mas vejo em alguns algoritmos o texto como "op e binary_op não invalidará iteradores ou subintervalos".Não é óbvio que o primeiro exemplo é seguro, porque a implementação mais simples
push_back
seria realocar primeiro o vetor, se necessário, e depois copiar a referência.Mas pelo menos parece seguro com o Visual Studio 2010. Sua implementação
push_back
faz um tratamento especial do caso quando você empurra um elemento no vetor. O código está estruturado da seguinte maneira:fonte
Isso não é uma garantia do padrão, mas como outro ponto de dados,
v.push_back(v[0])
é seguro para o libc ++ do LLVM .std::vector::push_back
chamadas do libc ++__push_back_slow_path
quando ele precisa realocar a memória:fonte
__swap_out_circular_buffer
, caso em que essa implementação é realmente segura.__swap_out_circular_buffer
. (Eu adicionei alguns comentários notar que.)A primeira versão definitivamente NÃO é segura:
da seção 17.6.5.9
Note-se que esta é a seção em corridas de dados, que as pessoas normalmente pensam em conjunto com enfiar ... mas a própria definição envolve "acontece antes" relacionamentos, e eu não vejo qualquer relação ordenação entre os vários efeitos colaterais de
push_back
em jogar aqui, ou seja, a invalidação de referência parece não ser definida como ordenada em relação à cópia-construção do novo elemento de cauda.fonte
v[0]
não é um iterador, da mesma forma,push_back()
não leva um iterador. Portanto, do ponto de vista do advogado de idiomas, seu argumento é nulo. Desculpe. Eu sei que a maioria dos iteradores são indicadores, e o ponto de invalidar um iterador é praticamente o mesmo que para as referências, mas a parte do padrão que você cita é irrelevante para a situação em questão.x.push_back(x[0])
é SEGURO.É completamente seguro.
No seu segundo exemplo você tem
o que não é necessário porque se o vetor sair do seu tamanho, isso implicará em
reserve
.Vector é responsável por essas coisas, não você.
fonte
Ambos são seguros, pois o push_back copiará o valor, não a referência. Se você estiver armazenando ponteiros, isso ainda é seguro no que diz respeito ao vetor, mas saiba que você terá dois elementos do vetor apontando para os mesmos dados.
As implementações de push_back devem, portanto, garantir que uma cópia de
v[0]
seja inserida. Por exemplo, assumindo que uma implementação seria realocada antes da cópia, ela certamente não acrescentaria uma cópiav[0]
e, como tal, violaria as especificações.fonte
push_back
no entanto, também redimensionará o vetor e, em uma implementação ingênua, isso invalidará a referência antes que a cópia ocorra. Portanto, a menos que você possa fazer o backup com uma citação do padrão, considerarei errado.push_back
copiará o valor no vetor; mas (tanto quanto posso ver) que pode acontecer após a realocação, quando a referência da qual está tentando copiar não é mais válida.push_back
recebe seu argumento por referência .A partir de 23.3.6.5/1:
Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Como estamos inserindo no final, nenhuma referência será invalidada se o vetor não for redimensionado. Portanto, se o vetor
capacity() > size()
for garantido, ele funcionará, caso contrário, será um comportamento indefinido.fonte
references
parte da citação.push_back
).