Il y a quelque temps, j'ai lu un article qui expliquait plusieurs pièges de la recherche dépendante de l'argument, mais je ne le trouve plus. Il s'agissait d'accéder à des choses auxquelles vous ne devriez pas avoir accès ou quelque chose du genre. J'ai donc pensé poser la question suivante: quels sont les pièges de l'ADL?
Réponse
Trop de publicités?Il y a un énorme problème avec l'argument-dépendante de recherche. Considérons, par exemple, l'utilitaire suivant:
#include <iostream>
namespace utility
{
template <typename T>
void print(T x)
{
std::cout << x << std::endl;
}
template <typename T>
void print_n(T x, unsigned n)
{
for (unsigned i = 0; i < n; ++i)
print(x);
}
}
C'est assez simple, non? Nous pouvons appeler, print_n()
et le passer n'importe quel objet et qu'il appellera print
d'imprimer l'objet n
temps.
En fait, il s'avère que si nous nous intéressons seulement à ce code, nous avons absolument aucune idée de ce que la fonction sera appelée par print_n
. Il pourrait être l' print
modèle de fonction est donnée ici, mais peut-être pas. Pourquoi? Argument dépendant de recherche.
Comme un exemple, disons que vous avez écrit une classe pour représenter une licorne. Pour une raison quelconque, vous avez également défini une fonction nommée print
(quelle coïncidence!) que seulement les causes de la panne du programme par écrit à un déréférencé pointeur null (qui sait pourquoi vous avez fait cela, ce n'est pas important):
namespace my_stuff
{
struct unicorn { /* unicorn stuff goes here */ };
std::ostream& operator<<(std::ostream& os, unicorn x) { return os; }
// Don't ever call this! It just crashes! I don't know why I wrote it!
void print(unicorn) { *(int*)0 = 42; }
}
Ensuite, vous écrivez un petit programme qui crée une licorne et l'imprime quatre fois:
int main()
{
my_stuff::unicorn x;
utility::print_n(x, 4);
}
Vous compilez ce programme, l'exécuter, et... il se bloque. "Quoi?! Pas question," vous dites: "je viens d'appeler print_n
, qui appelle l' print
la fonction d'impression de la licorne quatre fois!" Oui, c'est vrai, mais il n'a pas appelé l' print
fonction vous attend à l'appeler. Il s'appelle my_stuff::print
.
Pourquoi est - my_stuff::print
sélectionné? Lors de la recherche d'un nom, le compilateur voit que l'argument de l'appel à l' print
est de type unicorn
, ce qui est un type de classe qui est déclarée dans l'espace de noms my_stuff
.
En raison de l'argument dépendante de la recherche, le compilateur comprend cet espace de noms dans sa recherche de candidat pour les fonctions nommées print
. Il conclut my_stuff::print
, qui est ensuite choisi comme le meilleur candidat viable au cours de la résolution de surcharge: aucune conversion n'est nécessaire pour l'appeler de la candidate print
fonctions et nontemplate fonctions sont privilégiées pour les modèles de fonction, de sorte que le nontemplate fonction my_stuff::print
correspond le mieux.
(Si vous ne croyez pas cela, vous pouvez compiler le code dans cette question-est et voir ADL en action).
Oui, l'argument dépendant de recherche est une caractéristique importante de C++. Il est essentiellement nécessaire pour obtenir le comportement souhaité de certaines fonctionnalités de langage comme la surcharge des opérateurs (considérer le flux de la bibliothèque). Cela dit, il est aussi très, très imparfait et peut conduire à vraiment laid problèmes. Plusieurs propositions ont été formulées pour corriger argument dépendant de recherche, mais aucun d'entre eux ont été acceptés par le C++ comité de normalisation.