Quelles sont quelques bonnes explications sur ce qu'est la recherche dépendante des arguments? Beaucoup de gens l'appellent également Koenig Lookup.
De préférence, j'aimerais savoir:
- Pourquoi est-ce une bonne chose?
- Pourquoi est-ce une mauvaise chose?
- Comment ça marche?
c++
argument-dependent-lookup
name-lookup
c++-faq
user965369
la source
la source
std::cout << "Hello world";
ne compileraitRéponses:
Koenig Lookup , ou Argument Dependent Lookup , décrit comment les noms non qualifiés sont recherchés par le compilateur en C ++.
La norme C ++ 11 § 3.4.2 / 1 stipule:
En termes plus simples, Nicolai Josuttis déclare 1 :
Un exemple de code simple:
Dans l'exemple ci-dessus, il n'y a ni
using
-declaration niusing
-directive mais le compilateur identifie toujours correctement le nom non qualifiédoSomething()
comme la fonction déclarée dans l'espaceMyNamespace
de noms en appliquant la recherche Koenig .Comment ça marche?
L'algorithme dit au compilateur de regarder non seulement la portée locale, mais également les espaces de noms qui contiennent le type de l'argument. Ainsi, dans le code ci-dessus, le compilateur trouve que l'objet
obj
, qui est l'argument de la fonctiondoSomething()
, appartient à l'espace de nomsMyNamespace
. Donc, il regarde cet espace de noms pour localiser la déclaration dedoSomething()
.Quel est l'avantage de la recherche Koenig?
Comme le montre l'exemple de code simple ci-dessus, la recherche Koenig offre commodité et facilité d'utilisation au programmeur. Sans la recherche Koenig, il y aurait une surcharge sur le programmeur, pour spécifier à plusieurs reprises les noms
using
complets , ou à la place, utiliser de nombreuses déclarations.Pourquoi la critique de la recherche Koenig?
Une dépendance excessive à la recherche Koenig peut conduire à des problèmes sémantiques et parfois prendre le programmeur au dépourvu.
Prenons l'exemple de
std::swap
, qui est un algorithme de bibliothèque standard permettant d'échanger deux valeurs. Avec la recherche Koenig, il faudrait être prudent lors de l'utilisation de cet algorithme car:peut ne pas montrer le même comportement que:
Avec ADL, la version de la
swap
fonction appelée dépend de l'espace de noms des arguments qui lui sont passés.S'il existe un espace de noms
A
et siA::obj1
,A::obj2
&A::swap()
existent, le deuxième exemple entraînera un appel àA::swap()
, ce qui pourrait ne pas être ce que l'utilisateur voulait.En outre, si pour une raison quelconque les deux
A::swap(A::MyClass&, A::MyClass&)
etstd::swap(A::MyClass&, A::MyClass&)
sont définis, alors le premier exemple appellerastd::swap(A::MyClass&, A::MyClass&)
mais le second ne compilera pas carswap(obj1, obj2)
serait ambigu.Trivia:
Pourquoi s'appelle-t-il «Koenig lookup»?
Parce qu'il a été conçu par Andrew Koenig, ancien chercheur et programmeur d'AT & T et de Bell Labs .
Lectures complémentaires:
Recherche de nom de Herb Sutter sur GotW
Standard C ++ 03/11 [basic.lookup.argdep]: 3.4.2 Recherche de nom dépendante de l'argument.
1 La définition de la recherche Koenig est telle que définie dans le livre de Josuttis, The C ++ Standard Library: A Tutorial and Reference .
la source
std::swap
vous devez le faire puisque la seule alternative serait d'ajouterstd::swap
une spécialisation explicite de fonction de modèle pour votreA
classe. Pourtant, si votreA
classe est un modèle lui-même, ce serait une spécialisation partielle plutôt qu'une spécialisation explicite. Et la spécialisation partielle de la fonction de modèle n'est pas autorisée. L'ajout d'une surcharge destd::swap
serait une alternative mais est explicitement interdit (vous ne pouvez pas ajouter d'éléments à l'std
espace de noms). Donc ADL est le seul moyen pourstd::swap
.std::swap()
semble un peu à l'envers. Je m'attendrais à ce que le problème soit quandstd::swap()
est sélectionné plutôt que la surcharge spécifique au typeA::swap()
,. L'exemple avecstd::swap(A::MyClass&, A::MyClass&)
semble trompeur. commestd
n'aurait jamais de surcharge spécifique pour un type d'utilisateur, je ne pense pas que ce soit un bon exemple.MyNamespace::doSomething
, pas seulement::doSomething
.Dans Koenig Lookup, si une fonction est appelée sans spécifier son espace de noms, alors le nom d'une fonction est également recherché dans les espaces de noms dans lesquels le type du ou des arguments est défini. C'est pourquoi il est également connu sous le nom de recherche de nom dépendant de l' argument , en bref simplement ADL .
C'est à cause de Koenig Lookup, nous pouvons écrire ceci:
Sinon, il faudrait écrire:
ce qui est vraiment trop de frappe et le code a l'air vraiment moche!
En d'autres termes, en l'absence de Koenig Lookup, même un programme Hello World semble compliqué.
la source
std::cout
c'est un argument de la fonction, ce qui suffit pour activer ADL. Avez-vous remarqué cela?ostream<<
(comme dans ce qu'elle prend comme arguments et ce qu'elle renvoie). 2) Noms complets (commestd::vector
oustd::operator<<
). 3) Une étude plus détaillée de la recherche dépendante de l'argument.std::endl
comme argument, est en fait une fonction membre. Quoi qu'il en soit, si j'utilise à la"\n"
place destd::endl
, ma réponse est correcte. Merci pour le commentaire.f(a,b)
appelle une fonction libre . Donc, dans le cas destd::operator<<(std::cout, std::endl);
, il n'y a pas de telle fonction libre qui prendstd::endl
comme deuxième argument. C'est la fonction membre qui prendstd::endl
comme argument, et pour laquelle vous devez écrirestd::cout.operator<<(std::endl);
. et comme il y a une fonction libre qui prendchar const*
comme second argument,"\n"
fonctionne;'\n'
fonctionnerait également.Il vaut peut-être mieux commencer par le pourquoi, et ensuite seulement passer au comment.
Lorsque les espaces de noms ont été introduits, l'idée était que tout soit défini dans des espaces de noms, afin que des bibliothèques séparées n'interfèrent pas les unes avec les autres. Cependant, cela a introduit un problème avec les opérateurs. Regardez par exemple le code suivant:
Bien sûr, vous auriez pu écrire
N::operator++(x)
, mais cela aurait vaincu le point de surcharge des opérateurs. Par conséquent, il fallait trouver une solution qui permettait au compilateur de trouveroperator++(X&)
malgré le fait qu'elle n'était pas dans la portée. D'un autre côté, il ne devrait toujours pas en trouver un autreoperator++
défini dans un autre espace de noms non lié qui pourrait rendre l'appel ambigu (dans cet exemple simple, vous n'obtiendrez pas d'ambiguïté, mais dans des exemples plus complexes, vous pourriez). La solution était la recherche dépendante de l'argument (ADL), appelée ainsi puisque la recherche dépend de l'argument (plus exactement, du type de l'argument). Depuis que le schéma a été inventé par Andrew R. Koenig, il est également souvent appelé recherche Koenig.L'astuce est que pour les appels de fonction, en plus de la recherche de nom normale (qui trouve les noms dans la portée au point d'utilisation), il y a une seconde recherche dans les portées des types de tous les arguments donnés à la fonction. Ainsi , dans l'exemple ci - dessus, si vous écrivez
x++
dans le principal, il chercheoperator++
non seulement une portée mondiale, mais en plus dans le champ où le type dex
,N::X
, a été défini, par exemple dansnamespace N
. Et là, il trouve une correspondanceoperator++
, et doncx++
fonctionne tout simplement. Un autreoperator++
défini dans un autre espace de noms, par exempleN2
, ne sera pas trouvé, cependant. Puisque ADL n'est pas limité aux espaces de noms, vous pouvez également utiliser à laf(x)
place deN::f(x)
inmain()
.la source
À mon avis, tout n'est pas bon. Les gens, y compris les vendeurs de compilateurs, l'ont insulté à cause de son comportement parfois malheureux.
ADL est responsable d'une refonte majeure de la boucle for-range en C ++ 11. Pour comprendre pourquoi ADL peut parfois avoir des effets inattendus, considérez que non seulement les espaces de noms dans lesquels les arguments sont définis sont pris en compte, mais aussi les arguments des arguments modèles des arguments, des types de paramètres des types de fonction / des types pointee des types de pointeurs de ces arguments , et ainsi de suite.
Un exemple utilisant boost
Cela entraînait une ambiguïté si l'utilisateur utilise la bibliothèque boost.range, car les deux
std::begin
sont trouvés (par ADL en utilisantstd::vector
) etboost::begin
sont trouvés (par ADL en utilisantboost::shared_ptr
).la source
std::begin
efface l'ambiguïté de l'espace de noms.