Voilà j'ai une classe NbgItem qui a cette gueule là (j'ai simplifié), elle est construite par lecture d'un sous-fichier binaire, c'est une classe de stockage de très bas niveau, à plus haut niveau je construis des classes spécifiques pour extraire le contenu.
class NbgItem
{
public:
/* des trucs sans importance pour le problème */
bool isMemory() const;
bool isSimu() const;
};
Je stocke ces machins dans un vector tout ce qu'il y a de plus classique
using NbgInstance = std::vector<NbgItem>;
Maintenant j'ai une fonction de sélection des NbgItem qui m'intéressent qui ressemble à ça
template<class P>
std::vector<NbgItem const *>
select(NbgInstance const & set, P p)
{
std::vector<NbgItem const *> result {};
for( auto const & i : set){
if (p(i)){
result.push_back(&i);
}
}
return result;
}
Dans une classe qui formate l'affichage de ces bidules, les simu et les memory s"affichent exactement de la même façon, c'est leur signification qui est différente, aussi je n'ai qu'une seule classe pour afficher les deux, j'affiche simplement le seul sous-ensemble qui m'intéresse via une fonction update
void update(NbgInstance const & inst,bool mem)
{
auto f = mem ? [] (NbgItem const & i){return i.isMemory();}
: [] (NbgItem const & i){return i.isSimu();}
auto result = select(inst,f);
// La suite ...
}
Cette fonction compile avec gcc 5.3 mais pas avec VS2017, pour passer avec VS 2017, je dois la transformer en
void update(NbgInstance const & inst,bool mem)
{
auto f = std::function<bool (NbgItem const &)>;
if (mem){
f = [](NbgItem const & i ){return i.isMemory();};
} else {
f = [](NbgItem const & i ){return i.isSimu();};
}
auto result = select(inst,f);
// la suite ...
}
Ce n'est pas que ça me gêne de devoir passer par un if plutôt que par le ternaire, mais je trouve la solution du ternaire plus élégante, est ce que la solution du ternaire vous paraît aller à l'encontre de la norme ?
De mémoire, le type d'une lambda est quelque chose de non-spécifié par la norme. Donc je dirai qu'il n'y a pas de raison que le compilateur soit obligé de donner le même type à tes deux lambdas, donc la ternaire devient ill-formed parce que le type dans les deux branches n'est pas le même.
Quand j'ai écris le code, j'étais sur GCC, et j'étais pas sûr que ce soit valide. Je l'ai tenté quand même et c'est passé. C'est quand j'ai voulu le passer sur VS que ça ne voulait plus compiler. Quoi qu'il en soit la solution avec des if passe sur les deux compilos et donne les résultats attendus, ça me va bien
Ca me semble etre possible comme explication. Il suffit que MSVC donne un nom différent en interne (et pas juste la signature) pour qu'il puisse considérer que c'est 2 fonctions differentes.
A noter, dans le second code, std::function n'est pas le type correspondant a une lambda, c'est un type qui peut encapsuler une lambda. Il y a un cast pour mettre une lambda dans un std::function. Tu peux utiliser le ternaire, mais il faudra forcer le type.
auto f = mem ?
std::function<bool (NbgItem const &)>([](NbgItem const & i ){return i.isMemory();}) :
std::function<bool (NbgItem const &)>([](NbgItem const & i ){return i.isSimu();});
Ca devrait fonctionner aussi avec un pointer de fonction.
Vc ne sait pas faire de double conversions vers un sous type. Ici, il y a 2 lambdas donc 2 types différents. Une lambda sans contexte de capture peut être implicitement convertit en pointeur de fonction et clang / gcc font bien la conversion vers le type commun. Vc se vautre.
Ceci devrait fonctionner sans utiliser std::function.
auto* f = [](NbgItem const & i ){return i.isMemory();};
if (!mem){
f = [](NbgItem const & i ){return i.isSimu();};
}
Au pire, tu mets la ternaire dans la condition, ce ne sera pas pire qu'utiliser un pointeur de fonction.
- Edité par jo_link_noir 7 septembre 2018 à 20:47:22
Salut, pour compléter la réponse de jo_link_noir, il faut savoir que les lambda sans contexte de capture possède l'opérateur "+" qui permet de convertir une lambda en pointeur de fonction.
Du coup quelque chose comme :
auto f = mem ? +[] (NbgItem const & i){return i.isMemory();}
: +[] (NbgItem const & i){return i.isSimu();}
Devrait fonctionner et faire l'affaire.
- Edité par Qnope 7 septembre 2018 à 23:15:00
http://cpp-rendering.io : Vous trouverez tout ce dont vous avez besoin sur Vulkan / OpenGL et le rendu 3D !
Bon, en fait, c'est pas un opérateur spéciale pour les lambdas, c'est l'operateur uniaire +, qui a une surchage qui prend un pointeur de fonction et retourne un pointeur de fonction. Et la lambda est casté en pointeur de fonction pour appeler cet opérateur, ce qui permet le cast.
(Le code est valide, mais dire "les lambdas possédent un operateur +" est bof bof)
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Discord NaN. Mon site.
Discord NaN. Mon site.
Par contre, j'ignorais qu'on pouvait faire ça sur une lambda