Quais são algumas boas explicações sobre o que é a pesquisa dependente de argumento? Muitas pessoas também chamam de Koenig Lookup.
De preferência, gostaria de saber:
- Por que isso é uma coisa boa?
- Por que isso é uma coisa ruim?
- Como funciona?
c++
argument-dependent-lookup
name-lookup
c++-faq
user965369
fonte
fonte
std::cout << "Hello world";
não seria compiladoRespostas:
A Pesquisa Koenig , ou Pesquisa Dependente de Argumento , descreve como os nomes não qualificados são pesquisados pelo compilador no C ++.
O padrão C ++ 11, § 3.4.2 / 1, declara:
Em termos mais simples, Nicolai Josuttis afirma 1 :
Um exemplo de código simples:
No exemplo acima, não há uma
using
declaração-nem um diretório,using
mas ainda assim o compilador identifica corretamente o nome não qualificadodoSomething()
como a função declarada no espaço para nomeMyNamespace
aplicando a pesquisa Koenig .Como funciona?
O algoritmo diz ao compilador para não apenas examinar o escopo local, mas também os espaços para nome que contêm o tipo do argumento. Assim, no código acima, o compilador descobre que o objeto
obj
, que é o argumento da funçãodoSomething()
, pertence ao espaço para nomeMyNamespace
. Portanto, ele olha para esse espaço para nome para localizar a declaração dedoSomething()
.Qual é a vantagem da pesquisa Koenig?
Como o exemplo de código simples acima demonstra, a pesquisa Koenig oferece conveniência e facilidade de uso ao programador. Sem a pesquisa de Koenig, haveria uma sobrecarga no programador, para especificar repetidamente os nomes totalmente qualificados ou, em vez disso, usar várias
using
declarações -.Por que as críticas à pesquisa de Koenig?
O excesso de confiança na pesquisa de Koenig pode levar a problemas semânticos e, às vezes, pegar o programador desprevenido.
Considere o exemplo de
std::swap
, que é um algoritmo de biblioteca padrão para trocar dois valores. Com a pesquisa de Koenig, seria preciso ter cuidado ao usar esse algoritmo, porque:pode não mostrar o mesmo comportamento que:
Com o ADL, qual versão da
swap
função é chamada dependeria do espaço para nome dos argumentos passados para ele.Se existe um espaço de nomes
A
e seA::obj1
,A::obj2
eA::swap()
existem, em seguida, o segundo exemplo irá resultar em uma chamada paraA::swap()
, que pode não ser o que o usuário queria.Além disso, se por algum motivo tanto
A::swap(A::MyClass&, A::MyClass&)
estd::swap(A::MyClass&, A::MyClass&)
são definidos, em seguida, o primeiro exemplo chamarástd::swap(A::MyClass&, A::MyClass&)
mas o segundo não irá compilar, porqueswap(obj1, obj2)
seria ambíguo.Curiosidades:
Por que é chamado de "pesquisa Koenig"?
Porque foi desenvolvido pelo ex-pesquisador e programador da AT&T e Bell Labs, Andrew Koenig .
Leitura adicional:
Pesquisa de nome de Herb Sutter no GotW
Padrão C ++ 03/11 [basic.lookup.argdep]: 3.4.2 Pesquisa de nome dependente de argumento.
1 A definição da pesquisa de Koenig é a definida no livro de Josuttis, The C ++ Standard Library: A Tutorial and Reference .
fonte
std::swap
você, na verdade, é necessário fazer isso, pois a única alternativa seria adicionar astd::swap
função de modelo de especialização explícita para suaA
classe. No entanto, se suaA
classe for um modelo em si, seria especialização parcial, e não explícita. E a especialização parcial da função de modelo não é permitida. Adicionar sobrecarga destd::swap
seria uma alternativa, mas é explicitamente proibido (você não pode adicionar coisas aostd
espaço para nome). Portanto, o ADL é o único caminhostd::swap
.std::swap()
parece um pouco para trás. Eu esperaria que o problema seja quandostd::swap()
for selecionado, e não a sobrecarga específica para o tipoA::swap()
. O exemplo comstd::swap(A::MyClass&, A::MyClass&)
parece enganador. comostd
nunca haveria uma sobrecarga específica para um tipo de usuário, não acho que seja um ótimo exemplo.MyNamespace::doSomething
, não apenas::doSomething
.Na Pesquisa Koenig, se uma função é chamada sem especificar seu espaço para nome, o nome de uma função também é pesquisado no (s) espaço (s) de nomes no qual o tipo de argumento (s) está definido. É por isso que também é conhecido como pesquisa de nome dependente de argumento , em resumo simplesmente ADL .
É por causa da pesquisa Koenig, podemos escrever o seguinte:
Caso contrário, teríamos que escrever:
o que realmente é muita digitação e o código parece realmente feio!
Em outras palavras, na ausência do Koenig Lookup, até mesmo um programa Hello World parece complicado.
fonte
std::cout
é um argumento para a função, o suficiente para ativar a ADL. Você percebeu isso?ostream<<
(como no que leva como argumento e no que ele retorna). 2) Nomes totalmente qualificados (comostd::vector
oustd::operator<<
). 3) Um estudo mais detalhado da Pesquisa Dependente de Argumento.std::endl
usada como argumento é na verdade uma função de membro. Enfim, se eu usar em"\n"
vez destd::endl
, então minha resposta está correta. Obrigado pelo comentário.f(a,b)
invoca uma função livre . Portanto, no caso destd::operator<<(std::cout, std::endl);
, não existe essa função livre que levastd::endl
como segundo argumento. É a função de membro que assumestd::endl
como argumento e para a qual você deve escreverstd::cout.operator<<(std::endl);
. e uma vez que existe uma função livre que assumechar const*
como segundo argumento,"\n"
funciona;'\n'
funcionaria também.Talvez seja melhor começar com o porquê e só depois ir para o como.
Quando os espaços para nome foram introduzidos, a idéia era ter tudo definido nos espaços para nome, para que bibliotecas separadas não interferissem entre si. No entanto, isso introduziu um problema com os operadores. Veja, por exemplo, o seguinte código:
Claro que você poderia ter escrito
N::operator++(x)
, mas isso teria derrotado todo o ponto de sobrecarga do operador. Portanto, uma solução teve que ser encontrada, o que permitiu ao compilador encontrar,operator++(X&)
apesar de não estar no escopo. Por outro lado, ainda não deve encontrar outrooperator++
definido em outro espaço de nome não relacionado que possa tornar a chamada ambígua (neste exemplo simples, você não obteria ambiguidade, mas em exemplos mais complexos, talvez). A solução foi a Pesquisa Dependente de Argumento (ADL), chamada dessa maneira, pois a pesquisa depende do argumento (mais exatamente, do tipo do argumento). Desde que o esquema foi inventado por Andrew R. Koenig, também é chamado de pesquisa Koenig.O truque é que, para chamadas de função, além da pesquisa normal de nomes (que encontra nomes no escopo no ponto de uso), é feita uma segunda pesquisa nos escopos dos tipos de argumentos fornecidos à função. Assim, no exemplo acima, se você escrever
x++
no principal, ele procuraoperator++
não só no âmbito global, mas além disso, no âmbito onde o tipo dex
,N::X
, foi definida, ou seja,namespace N
. E lá encontra uma correspondênciaoperator++
e, portanto,x++
simplesmente funciona. Outrooperator++
definido em outro espaço para nome, por exemploN2
, não será encontrado. Como a ADL não está restrita a espaços para nome, você também pode usar emf(x)
vez deN::f(x)
emmain()
.fonte
Nem tudo é bom, na minha opinião. Pessoas, incluindo fornecedores de compiladores, o insultam por causa de seu comportamento às vezes infeliz.
A ADL é responsável por uma grande revisão do loop for-range no C ++ 11. Para entender por que a ADL às vezes pode ter efeitos não intencionais, considere que não apenas os espaços para nome em que os argumentos são definidos são considerados, mas também os argumentos dos argumentos de modelo dos argumentos, dos tipos de parâmetros dos tipos de função / tipos de ponta dos tipos de ponteiro desses argumentos e assim por diante.
Um exemplo usando boost
Isso resultou em uma ambiguidade se o usuário usar a biblioteca boost.range, porque ambos
std::begin
são encontrados (pelo ADL usandostd::vector
) eboost::begin
encontrados (pelo ADL usandoboost::shared_ptr
).fonte
std::begin
limpa a ambiguidade do espaço para nome.