class Base
{};
class Foo :
public Base
{};
class Bar :
public Base
{};
void test(const Base&)
{
std::cout << "class Base" << std::endl;
}
void test(const Bar&)
{
std::cout << "class Bar" << std::endl;
}
int main()
{
Base base;
test(base);
Foo foo;
test(foo);
Bar bar;
test(bar);
}
J'obtiens la sortie suivante:
class Base
class Base
class Bar
Rien à redire, grâce au mécanisme des surcharges, on peut appeler une fonction spécialisée en fonction du type concret du paramètre.
Peut-on obtenir le même résultat avec des lambdas ?
Pour autant que, comme l'a si bien dit lmghs, tu parle bien de redéfinition et non de surcharge et, bien sur, que tu utilises les référence (ou, au pire, les pointeurs) pour les paramètres polimorphes, oui, bien sur, en vertu du LSP
Car Foo EST-UNE Base, ce qui implique qu'une instance de Foo peut être transmise à n'importe quelle fonction s'attendant à recevoir une (référence ou un pointeur sur une) instance de Base.
Grâce au principe de la meilleure correspondance (best match), nous pouvons donc être sur que notre instance de Bar va faire appeler la version s'attendant à recevoir une référence sur une Bar, ce qui est parfaitement logique.
Par contre, Bar a beau être également une Base, Foo n'est absolument pas une Bar, et donc, la meilleure correspondance va devoir s'arrêter au niveau de Base pour ce qui concerne Foo.
Que la fonction en question soit une fonction libre, une fonction membre, une fonction membre statique ou une expression lambda ne changera absolument rien: le compilateur essayera toujours de faire appel à la version qui correspond le mieux à l'argument que l'on va transmettre (dans la limite des héritages dont il aura connaissance).
La véritable question à se poser aurait plutôt trait à la raison pour laquelle tu tiendrais absolument à utiliser une expression lambda plutôt qu'une fonction "classique".
Je ne suis, en effet, absolument pas convaincu qu'il y ait un réel intérêt à utiliser les expressions lambda dans ce cas de figure car, en dehors des cas où l'on sait exactement quel est le type réel de notre instance (comme c'est le cas pour le code que tu montre dans ta question), ce "best match" s'applique parfaitement dans le cadre du deuxième appel effectué lors d'un double dispatch
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Salut ! Je crois qu'il est impossible de redéfinir une lambda en c++. Donc non tu ne peux pas spécifier un comportement différent suivant le type d'argument passé à une lambda. Même les lambda template introduites en c++20 ne le permettent pas vraiment... de ce que j'en ai compris.
Rien à redire, grâce au mécanisme des surcharges, on peut appeler une fonction spécialisée en fonction du type concret du paramètre.
Ton compilateur te ment !
Pour qu'on soit d'accord sur le vocabulaire (j'ai un doute, lmghs pourra me corriger). C'est du rappel, je sais que tu sais, mais c'est pour que les choses soient claires (et éventuellement me corriger).
Quand tu as :
A* p = new B;
- B est le type dynamique ou type concret, c'est à dire type de l'objet tel qu'il a été créé en mémoire
- A est le type statique, c'est à dire le type que l'on "voit" dans le code.
Cette distinction intervient dans un héritage quand on veut utiliser une fonction virtuelle d'un objet qu'on manipule par référence ou pointeur.
struct A {
virtual void foo();
void bar();
}
struct B : A {
void foo() override;
void bar();
};
void f(A* a) {
a->foo();
a->bar();
}
A a;
f(&a);
B b;
f(&a);
Dans ce code, la fonction f() est appelée avec un type static A, mais avec un type dynamique A dans un premier cas et B dans un second cas. La virtualisation de foo() va faire que A::foo() va être appelé dans le premier cas, et B::foo() dans le second. Par contre, c'est A::bar() qui est appelé dans le 2 cas.
Le fonctionnement de foo() est ce qu'on appelle le simple dispatch. En fonction d'un type dynamique, on va appeler différentes fonctions au runtime.
Le problème de ton code, c'est que les types statiques et dynamiques sont les mêmes à chaque appel de fonction. Ca donne l'impression d'un simple dispatch, mais ce n'est pas le cas : il n'y a pas résolution des appels de fonctions au runtime. Tout est fait au compile time !
Pour voir le simple dispatch agir, il faut que le résolution des fonctions soient au runtime. Tu peux par exemple écrire :
int main()
{
volatile int i = 2;
Base* b = nullptr;
if (i == 0)
b = new Base;
else if (i == 1)
b = new Foo;
else
b = new Bar;
test(*b);
}
Si tu fais cela, tu verras que cela affiche toujours "Base". Tout simplement parce que c'est le type statique utilisés pour l'appel de fonctions par le compilateur.
Dit autrement, le simple dispatch ne fonctionne pas avec la surcharge (overloading). C'est la redéfinition (override) qui permet, via le mécanisme des v-tables, de faire un simple dispatch. (D'où la question de lmghs pour lever la confusion surcharge vs redéfinition).
Et donc, pour répondre à ta question, le simple dispatch ne va pas fonctionner avec des lambdas, puisque cela ne fonctionne pas du tout avec la surcharge en règle générale.
-----------------------------
Je suppose que ta confusion vient du fait que l'on utilise effectivement la surcharge quand on fait du double dispatch avec le pattern visitor. Si tu regardes le code C++ de l'article "double dispatch" de wikipedia https://en.wikipedia.org/wiki/Double_dispatch, ce code utilise effectivement la surcharge. Mais dans les explications, on voit que le code donné correspond à une simple dispatch et le code pour le double dispatch est expliqué ensuite (mais pas donné).
Ce qu'on aimerait faire, quand on veut faire du double dispatch, c'est :
Mais ce code ne fonctionne pas, parce que la résolution de foo() va se faire statiquement, c'est à dire avec le type B1 et pas T1.
Avec le pattern visitor, on ajoute une couche d'indirection, qui contient elle-même des fonctions virtuelles. Et c'est le fait d'avoir ces 2 v-tables qui permet de faire le double dispatch. Sans v-table, pas de dispatch.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Discord NaN. Mon site.