O programa a seguir está incorreto ,
inline constexpr auto makeFoo = [](auto const& x) {
return getFoo(x);
};
inline constexpr auto getFoo = [](int const&) {
return 1;
};
int bar() {
return makeFoo(32);
}
e reverter as definições de makeFoo
e getFoo
torna-o correto .
Entendo que, com base no entendimento "informal" da pesquisa de nomes em duas fases:
makeFoo
é um lambda genérico e,getFoo
em seu corpo, é invocado em um argumento cujo tipo ainda não é conhecido, então o nomegetFoo
ainda não pode ser consultado;- quando a chamada
makeFoo(32)
é vista, o lambdaoperator()
é intantiado comauto = int
, então a busca de segunda fase pelo nome não qualificadogetFoo
acontece; - isso, no entanto, é apenas ADL, então não encontra
getFoo
, porque esse é o nome de um lambda, ou seja, um objeto, não de uma função.
Alguém pode me orientar sobre as partes do rascunho padrão que "espelham" a explicação acima?
Suponho que [basic.lookup.unqual] e [temp.dep.res] possivelmente contenham tudo o que é necessário para explicar o caso, mas não consigo juntar as peças.
O que a pesquisa dependente de argumento faz é:
Mesmo sem focar nos detalhes de onde estamos procurando, a pesquisa dependente de argumento só encontrará declarações de funções e modelos de função.
getFoo
não é uma função nem um modelo de função, é um objeto — então nunca será encontrado.Então, se você quer
getFoo
ser encontrado, precisa ser feito por meio de uma pesquisa regular — cuja declaração deve preceder sua chamada.getFoo(x)
é uma chamada dependente . Conforme afirma a Nota 1 de [temp.dep.general] , e como explica [temp.res.general] :Isso basicamente significa que o nome
getFoo
não fica imediatamente malformadomakeFoo
porque a busca pelo nome está sendo atrasada.O
auto const&
parâmetro criamakeFoo
um lambda genérico e, portanto, o operador de chamada do tipo de fechamento é um modelo. O efeito é como se você escrevesse algo como:Isso é problemático por causa de [temp.dep.candidate] p1 :
getFoo
é um nome que aparece na declaração de um modelo e, portanto, é consultado emmakeFoo
, não posteriormente, ondegetFoo
seria visível. Isso torna seu programa malformado ao instanciaroperator()<int>()
.Nota sobre pesquisa dependente de argumento
Como você apontou, o ADL possui regras especiais, que tornariam o programa válido se ele
getFoo
fosse encontrado pelo ADL. Essas regras especiais podem ser encontradas em [basic.lookup.argdep] p4.