O COW é basic_string
proibido em C ++ 11 e posterior?
A respeito de
” Estou correto que C ++ 11 não admite implementações baseadas em COW std::string
?
Sim.
A respeito de
”Em caso afirmativo, esta restrição está explicitamente declarada em algum lugar do novo padrão (onde)?
Quase diretamente, por requisitos de complexidade constante para uma série de operações que exigiriam O ( n ) cópia física dos dados da string em uma implementação COW.
Por exemplo, para as funções de membro
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
... que em uma implementação de COW, ambos acionariam a cópia dos dados da string para não compartilhar o valor da string, o padrão C ++ 11 requer
C ++ 11 §21.4.5 / 4 :
” Complexidade: tempo constante.
… Que exclui a cópia de dados e, portanto, COW.
C ++ 03 suportado implementações da vaca por não ter esses requisitos constantes complexidade, e, sob certas condições restritivas, permitindo chamadas para operator[]()
, at()
, begin()
, rbegin()
, end()
, ou rend()
para referências invalidar os ponteiros e iterators referentes aos itens corda, ou seja, possivelmente incorrer em uma Cópia de dados COW. Este suporte foi removido no C ++ 11.
O COW também é proibido por meio das regras de invalidação do C ++ 11?
Em outra resposta que no momento da redação foi selecionada como solução, e que foi fortemente votada e, portanto, aparentemente acreditada, afirma-se que
” Para uma string COW, chamar não- const
operator[]
exigiria fazer uma cópia (e invalidar as referências), o que não é permitido pelo parágrafo [citado] acima [C ++ 11 §21.4.1 / 6]. Portanto, não é mais legal ter uma string COW em C ++ 11.
Essa afirmação é incorreta e enganosa de duas maneiras principais:
- Indica incorretamente que apenas os
const
acessadores de não itens precisam acionar uma cópia de dados COW.
Mas também os const
acessadores de item precisam acionar a cópia de dados, porque eles permitem que o código do cliente forme referências ou ponteiros que (em C ++ 11) não é permitido invalidar posteriormente por meio das operações que podem acionar a cópia de dados COW.
- Ele assume incorretamente que a cópia de dados COW pode causar invalidação de referência.
Mas, em uma implementação correta, a cópia de dados COW, não compartilhando o valor da string, é feita em um ponto antes que haja qualquer referência que possa ser invalidada.
Para ver como uma implementação COW correta do C ++ 11 basic_string
funcionaria, quando os requisitos O (1) que tornam isso inválido são ignorados, pense em uma implementação em que uma string pode alternar entre as políticas de propriedade. Uma instância de string começa com política compartilhável. Com esta política ativa, não pode haver referências de itens externos. A instância pode fazer a transição para a política Exclusiva e deve fazer isso quando uma referência de item é potencialmente criada, como com uma chamada para .c_str()
(pelo menos se isso produzir um ponteiro para o buffer interno). No caso geral de várias instâncias compartilhando a propriedade do valor, isso envolve a cópia dos dados da string. Após essa transição para a política Exclusiva, a instância só pode fazer a transição de volta para Compartilhável por uma operação que invalida todas as referências, como atribuição.
Portanto, embora a conclusão dessa resposta, de que as sequências de COW estão descartadas, seja correta, o raciocínio oferecido é incorreto e fortemente enganoso.
Suspeito que a causa desse mal-entendido seja uma nota não normativa no anexo C do C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings], sobre §21.3:
Alteração : os basic_string
requisitos não permitem mais sequências de contagem de referência
Justificativa: A invalidação é sutilmente diferente com sequências de contagem de referência. Esta mudança regulariza o comportamento (sic) para esta Norma.
Efeito na característica original: o código C ++ 2003 válido pode ser executado de forma diferente nesta Norma
Aqui, a justificativa explica o principal motivo pelo qual alguém decidiu remover o suporte COW especial do C ++ 03. Este raciocínio, o porquê , não é como o padrão efetivamente desautoriza a implementação de COW. O padrão não permite COW através dos requisitos O (1).
Resumindo, as regras de invalidação do C ++ 11 não excluem uma implementação COW de std::basic_string
. Mas eles descartam uma implementação COW irrestrita no estilo C ++ 03, razoavelmente eficiente, como aquela em pelo menos uma das implementações de biblioteca padrão do g ++. O suporte COW especial C ++ 03 permitiu eficiência prática, em particular usando const
acessores de item, ao custo de regras sutis e complexas para invalidação:
C ++ 03 §21.3 / 5, que inclui suporte COW de "primeira chamada":
” Referências, ponteiros e iteradores referentes aos elementos de uma basic_string
sequência podem ser invalidados pelos seguintes usos desse basic_string
objeto:
- Como um argumento para funções não-membro swap()
(21.3.7.8), operator>>()
(21.3.7.9) e getline()
(21.3. 7,9).
- Como um argumento para basic_string::swap()
.
- Funções de chamada data()
e c_str()
membro.
- Chamando não const
funções de membro, exceto operator[]()
, at()
, begin()
, rbegin()
, end()
, e rend()
.
- subsequente com qualquer uma das utilizações acima, excepto as formas de insert()
e erase()
que iterators regresso, a primeira chamada para não const
funções membro operator[]()
, at()
, begin()
, rbegin()
,end()
, ou rend()
.
Essas regras são tão complexas e sutis que duvido que muitos programadores, se houver, possam fornecer um resumo preciso. Eu não pude.
E se os requisitos O (1) forem desconsiderados?
Se os requisitos de tempo constante do C ++ 11 em, por exemplo, operator[]
forem desconsiderados, o COW para basic_string
poderia ser tecnicamente viável, mas difícil de implementar.
As operações que podem acessar o conteúdo de uma string sem incorrer na cópia de dados COW incluem:
- Concatenação via
+
.
- Saída via
<<
.
- Usando um
basic_string
como argumento para funções de biblioteca padrão.
Este último porque a biblioteca padrão pode contar com conhecimentos e construções específicas de implementação.
Além disso, uma implementação pode oferecer várias funções não padronizadas para acessar o conteúdo da string sem acionar a cópia de dados COW.
Um fator complicador principal é que no C ++ 11 o basic_string
acesso ao item deve acionar a cópia dos dados (cancelar o compartilhamento dos dados da string), mas é necessário não lançar , por exemplo, C ++ 11 §21.4.5 / 3 “ Lança: Nada.”. E, portanto, ele não pode usar a alocação dinâmica comum para criar um novo buffer para cópia de dados COW. Uma maneira de contornar isso é usar um heap especial onde a memória pode ser reservada sem ser realmente alocada e, em seguida, reservar a quantidade necessária para cada referência lógica a um valor de string. Reservar e cancelar a reserva em tal heap pode ser tempo constante, O (1), e alocar a quantidade que já foi reservada, pode sernoexcept
. A fim de cumprir os requisitos do padrão, com essa abordagem, parece que seria necessário um tal heap especial baseado em reserva por alocador distinto.
Notas:
¹ O const
acessador de item aciona uma cópia de dados COW porque permite que o código do cliente obtenha uma referência ou ponteiro para os dados, que não é permitido invalidar por uma cópia posterior de dados acionada, por exemplo, pelo const
acessador de não item.