Todo contêiner padrão possui um método begin
e end
para retornar iteradores para esse contêiner. No entanto, o C ++ 11 aparentemente introduziu funções gratuitas chamadas std::begin
e std::end
que chamam as funções de membro begin
e end
. Então, ao invés de escrever
auto i = v.begin();
auto e = v.end();
você escreveria
auto i = std::begin(v);
auto e = std::end(v);
Em sua palestra, Writing Modern C ++ , Herb Sutter diz que você deve sempre usar as funções livres agora quando quiser o iterador inicial ou final de um contêiner. No entanto, ele não entra em detalhes sobre o motivo pelo qual você gostaria. Observando o código, você economiza todos os caracteres. Portanto, no que diz respeito aos contêineres padrão, as funções livres parecem ser completamente inúteis. Herb Sutter indicou que havia benefícios para recipientes fora do padrão, mas, novamente, ele não entrou em detalhes.
Portanto, a questão é o que exatamente as versões de funções livres std::begin
e std::end
além de chamar suas versões correspondentes de funções-membro, e por que você deseja usá-las?
std::
o tempo todo.Respostas:
Como você chama
.begin()
e.end()
em uma matriz C?As funções livres permitem programação mais genérica porque podem ser adicionadas posteriormente, em uma estrutura de dados que você não pode alterar.
fonte
end
matrizes declaradas estaticamente (int foo[5]
) usando truques de programação de modelos. Depois de decair para um ponteiro, é claro que você está sem sorte.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
e obterend
uma matriz C, desde que você ainda não a tenha decaído para um ponteiro - @Huw explica. Quanto ao motivo pelo qual você desejaria: imagine que você refatorou o código que estava usando uma matriz para usar um vetor (ou vice-versa, por qualquer motivo). Se você estiver usandobegin
eend
, e talvez algum tipo inteligente de digitação, o código de implementação não precisará ser alterado (exceto talvez alguns dos typedefs).Considere o caso quando você tiver uma biblioteca que contenha classe:
tem 2 métodos:
para iterar sobre os valores que você precisa herdar dessa classe e definir
begin()
eend()
métodos para casos em queMas se você sempre usa
você consegue fazer isso:
onde
SpecialArrayIterator
é algo como:agora
i
ee
pode ser usado legalmente para iteração e acesso aos valores do SpecialArrayfonte
template<>
linhas. Você está declarando uma nova sobrecarga de função, não especializando um modelo.O uso das funções
begin
eend
free adiciona uma camada de indireção. Geralmente isso é feito para permitir mais flexibilidade.Neste caso, posso pensar em alguns usos.
O uso mais óbvio é para matrizes C (não ponteiros c).
Outra é quando se tenta usar um algoritmo padrão em um contêiner não conforme (ou seja, falta um
.begin()
método ao contêiner ). Supondo que você não possa consertar apenas o contêiner, a próxima melhor opção é sobrecarregar abegin
função. Herb sugere que você sempre use abegin
função para promover uniformidade e consistência no seu código. Em vez de ter que lembrar quais contêineres suportam o métodobegin
e quais precisam de funçãobegin
.Como um aparte, rev a próxima C ++ deve copiar D's notação pseudo-membro . Se
a.foo(b,c,d)
não estiver definido, ele tentafoo(a,b,c,d)
. É apenas um pouco de açúcar sintático para ajudar-nos humanos pobres que preferem a disciplina do que a ordenação verbal.fonte
Para responder à sua pergunta, as funções livres begin () e end () por padrão não fazem mais do que chamar as funções de membro .begin () e .end () do contêiner. De
<iterator>
, incluído automaticamente quando você usa qualquer um dos contêineres padrão<vector>
, como<list>
, etc., você obtém:A segunda parte da sua pergunta é por que preferir as funções livres se tudo o que eles fazem é chamar as funções-membro de qualquer maneira. Isso realmente depende de que tipo de objeto
v
está no seu código de exemplo. Se o tipo de v for um tipo de contêiner padrão,vector<T> v;
não importa se você usa as funções free ou member, elas fazem a mesma coisa. Se seu objetov
for mais genérico, como no código a seguir:Em seguida, o uso das funções de membro quebra seu código para matrizes T = C, seqüências de caracteres C, enumerações etc. Ao usar as funções de não membro, você anuncia uma interface mais genérica que as pessoas podem facilmente estender. Usando a interface de função livre:
O código agora funciona com matrizes T = C e seqüências de caracteres C. Agora, escreva uma pequena quantidade de código do adaptador:
Também podemos fazer com que seu código seja compatível com enumerações iteráveis. Eu acho que o ponto principal de Herb é que o uso das funções gratuitas é tão fácil quanto o uso das funções membro, e isso dá ao seu código compatibilidade retroativa com tipos de sequência C e compatibilidade direta com tipos de sequência não-stl (e futuros-stl!), com baixo custo para outros desenvolvedores.
fonte
enum
ou qualquer outro tipo fundamental por referência; eles serão mais baratos de copiar do que indiretos.Um benefício
std::begin
estd::end
é que eles servem como pontos de extensão para implementar a interface padrão para classes externas.Se você deseja usar a
CustomContainer
classe com função for loop ou template baseada em intervalo, que espera.begin()
e.end()
métodos, obviamente precisará implementar esses métodos.Se a classe fornecer esses métodos, isso não será um problema. Quando isso não acontecer, você precisará modificá-lo *.
Isso nem sempre é viável, por exemplo, ao usar uma biblioteca externa, especialmente a comercial e a de código fechado.
Nessas situações,
std::begin
estd::end
é útil, pois é possível fornecer API do iterador sem modificar a própria classe, mas sobrecarregando as funções livres.Exemplo: suponha que você gostaria de implementar uma
count_if
função que utiliza um contêiner em vez de um par de iteradores. Esse código pode ficar assim:Agora, para qualquer classe que você gostaria de usar com esse costume
count_if
, você só precisa adicionar duas funções livres, em vez de modificar essas classes.Agora, o C ++ tem um mecanismo chamado Argl Dependent Lookup (ADL), que torna essa abordagem ainda mais flexível.
Em resumo, ADL significa que quando um compilador resolver uma função não qualificada (ou seja, função sem espaço para nome, como em
begin
vez destd::begin
), ele também considerará funções declaradas nos espaços para nome dos seus argumentos. Por exemplo:Neste caso, não importa que nomes qualificados são
some_lib::begin
esome_lib::end
- uma vez queCustomContainer
está emsome_lib::
muito, compilador usará essas sobrecargas nocount_if
.Essa também é a razão de ter
using std::begin;
eusing std::end;
entrarcount_if
. Isso nos permite usar não qualificadobegin
eend
, portanto, permitindo ADL e permitindo que o compilador escolhastd::begin
estd::end
quando nenhuma outra alternativa for encontrada.Podemos comer o cookie e ter o cookie - ou seja, ter uma maneira de fornecer uma implementação personalizada de
begin
/end
enquanto o compilador pode voltar aos padrões.Algumas notas:
Pelo mesmo motivo, existem outras funções semelhantes:
std::rbegin
/rend
,std::size
estd::data
.Como outras respostas mencionam, as
std::
versões possuem sobrecargas para matrizes nuas. Isso é útil, mas é simplesmente um caso especial do que descrevi acima.Usar
std::begin
e amigos é uma boa idéia ao escrever o código do modelo, porque isso os torna mais genéricos. Para não-modelo, você também pode usar métodos, quando aplicável.PS Estou ciente de que este post tem quase 7 anos. Me deparei com isso porque queria responder uma pergunta marcada como duplicada e descobri que nenhuma resposta aqui menciona ADL.
fonte
Enquanto as funções de não membro não fornecem nenhum benefício para os contêineres padrão, usá-los impõe um estilo mais consistente e flexível. Se em algum momento você desejar estender uma classe de contêiner não-std existente, prefira definir sobrecargas das funções livres, em vez de alterar a definição da classe existente. Portanto, para contêineres não std, eles são muito úteis e sempre usar as funções livres torna seu código mais flexível, pois você pode substituir o contêiner std por um contêiner não std com mais facilidade e o tipo de contêiner subjacente é mais transparente ao seu código, pois suporta uma variedade muito maior de implementações de contêineres.
Mas é claro que isso sempre deve ser ponderado adequadamente e a abstração excessiva também não é boa. Embora o uso das funções livres não exija muita abstração, ele quebra a compatibilidade com o código C ++ 03, que nessa idade jovem do C ++ 11 ainda pode ser um problema para você.
fonte
boost::begin()
/end()
, então não há nenhuma incompatibilidade verdadeira :)begin/end
). Então, eu consideraria uma incompatibilidade com o C ++ 03 puro também. Mas, como dito anteriormente, é uma incompatibilidade bastante pequena (e cada vez menor), pois o C ++ 11 (pelo menosbegin/end
em particular) está cada vez mais adotando.Por fim, o benefício está no código que é generalizado, de modo que é independente do contêiner. Ele pode operar em um
std::vector
, um array ou um intervalo sem alterações no próprio código.Além disso, os contêineres, mesmo os contêineres não pertencentes à propriedade, podem ser adaptados de forma que também possam ser usados de forma independente por código, usando acessadores não baseados em intervalo de membros.
Veja aqui para mais detalhes.
fonte