Por que o vetor de libc ++ <bool> :: const_reference não é bool?


92

Seção 23.3.7 Classe vector<bool>[vector.bool], parágrafo 1 afirma:

template <class Allocator> class vector<bool, Allocator> {
public:
    // types:
    typedef bool              const_reference;
    ...

No entanto, este programa falha ao compilar ao usar libc ++:

#include <vector>
#include <type_traits>

int
main()
{
    static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}

Além disso, observo que o padrão C ++ tem sido consistente nesta especificação desde o C ++ 98. Além disso, observo que a libc ++ não tem seguido esta especificação de forma consistente desde a primeira introdução da libc ++.

Qual é a motivação para esta não conformidade?

Respostas:


99

A motivação para esta extensão, que é detectável por um programa em conformidade e, portanto, não conforme, é fazer com que o vector<bool>comportamento seja mais semelhante vector<char>em relação às referências (const e não).

Introdução

Desde 1998, vector<bool>tem sido ridicularizado como "não exatamente um contêiner". O LWG 96 , um dos primeiros problemas do LWG, lançou o debate. Hoje, 17 anos depois, vector<bool>permanece praticamente inalterado.

Este artigo apresenta alguns exemplos específicos de como o comportamento de vector<bool>difere de todas as outras instanciações de vector, prejudicando assim o código genérico. No entanto, o mesmo artigo discute detalhadamente as excelentes propriedades de desempenho que vector<bool>podem ter se implementadas corretamente.

Resumo : vector<bool>não é um recipiente ruim. Na verdade, é bastante útil. Ele só tem um nome ruim.

De volta a const_reference

Conforme apresentado acima e detalhado aqui , o que é ruim vector<bool>é que ele se comporta de maneira diferente no código genérico do que em outras vectorinstanciações. Aqui está um exemplo concreto:

#include <cassert>
#include <vector>

template <class T>
void
test(std::vector<T>& v)
{
    using const_ref = typename std::vector<T>::const_reference;
    const std::vector<T>& cv = v;
    const_ref cr = cv[0];
    assert(cr == cv[0]);
    v[0] = 1;
    assert(true == cv[0]);
    assert(cr == cv[0]);  // Fires!
}

int
main()
{
    std::vector<char> vc(1);
    test(vc);
    std::vector<bool> vb(1);
    test(vb);
}

A especificação padrão diz que a declaração marcada // Fires!será acionada, mas apenas quando testfor executada com um vector<bool>. Quando executado com um vector<char>(ou qualquer vectoralém boolquando um não-padrão apropriado Té atribuído), o teste passa.

A implementação da libc ++ buscou minimizar os efeitos negativos de ter um vector<bool>comportamento diferente no código genérico. Uma coisa que ele fez para conseguir isso foi fazer vector<T>::const_referenceuma referência de proxy , exatamente como o especificado vector<T>::reference, exceto que você não pode atribuir por meio dele. Ou seja, no libc ++, vector<T>::const_referenceé essencialmente um ponteiro para o bit dentro do vector, em vez de uma cópia desse bit.

Em libc ++, o acima testpassa para ambos vector<char>e vector<bool>.

A que custo?

A desvantagem é que essa extensão é detectável, conforme mostrado na pergunta. No entanto, poucos programas realmente se preocupam com o tipo exato desse alias e mais programas se preocupam com o comportamento.

Qual é a motivação para esta não conformidade?

Para dar ao cliente libc ++ um comportamento melhor em código genérico, e talvez depois de testes de campo suficientes, proponha esta extensão para um futuro padrão C ++ para o aprimoramento de toda a indústria C ++.

Essa proposta pode vir na forma de um novo contêiner (por exemplo bit_vector) que tem praticamente a mesma API de hoje vector<bool>, mas com algumas atualizações, como as const_referencediscutidas aqui. Seguido pela descontinuação (e eventual remoção) da vector<bool>especialização. bitsettambém poderia usar uma pequena atualização neste departamento, por exemplo const_reference, adicionar e um conjunto de iteradores.

Ou seja, em retrospectiva bitseté para vector<bool>(que deve ser renomeado para bit_vector- ou qualquer outra coisa), como arrayé para vector. E a analogia deve ser verdadeira, quer estejamos falando ou não sobre boolcomo value_typede vectore array.

Existem vários exemplos de recursos C ++ 11 e C ++ 14 que começaram como extensões em libc ++. É assim que os padrões evoluem. A experiência de campo positiva real demonstrada exerce forte influência. O pessoal dos padrões é um grupo conservador quando se trata de alterar as especificações existentes (como deveriam ser). Adivinhar, mesmo quando você tem certeza de que está adivinhando corretamente, é uma estratégia arriscada para desenvolver um padrão reconhecido internacionalmente.


1
Pergunta: o rascunho recente da proposta sobre iteradores de proxy por @EricNiebler poderia / iria de alguma forma legitimar as extensões libc ++ e colocar vector<bool>em uma base mais de primeira classe?
— TemplateRex

Observação: Eu preferiria ter um vector_bool<Alloc>e um array_bool<N>para representar versões compactadas (incluindo iteradores de proxy de acesso aleatório iterando todos os bits) de vector<bool, Alloc>e array<bool, N>. No entanto, bitset<N>(e seu primo boost::dynamic_bitset<Alloc>) representam uma abstração diferente: a saber, versões compactadas de std::set<int>. Portanto, gostaria de ter, digamos, bit_array<N>e bit_vector<Alloc>ser os sucessores da franquia de bitset, com iteradores bidirecionais apropriados (iterando sobre os bits de 1, em vez de sobre todos os bits). Quais são seus pensamentos sobre isso?
— TemplateRex

5
Meu rascunho de proposta sobre iteradores de proxy faria vector<bool>um contêiner de acesso aleatório em conformidade com o padrão. Não seria vector<bool>uma boa ideia. :-) Eu concordo com Howard. Ele deveria ter sido chamado de outra coisa.
— Eric Niebler

1
Por que não existe uma maneira de cancelar as extensões libc ++ e obter um comportamento estritamente de conformidade? (Eu nem estou pedindo para tornar a conformidade o padrão, apenas uma forma de desabilitar as extensões do libc ++ para poder escrever código portátil). Como você sabe, eu fui mordido por extensões de tupla libc ++ no passado, e recentemente mordido pela extensão bitset :: const_reference.
— gnzlbg

5
@gnzlbg: Uma quantidade finita de recursos econômicos e temporais estava disponível para o desenvolvimento inicial de libc ++. Posteriormente, a implementação estava condenada a não deixar todos os usuários felizes. Dados os recursos disponíveis, compensações de engenharia foram feitas na tentativa de maximizar o número de usuários satisfeitos, maximizar os benefícios para a comunidade C ++ geral. Desculpe pela sua experiência. Observo que as extensões de tupla com as quais você se deparou estão agora no documento de trabalho C ++ 1z atual. Sobre esse assunto, você sem saber se sacrificou para que muitos pudessem se beneficiar, e muitos têm uma dívida de gratidão com você.
— Howard Hinnant de
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.