tr1 :: mem_fn et tr1 :: bind: sur la correction de const et la surcharge
-
10-07-2019 - |
Question
Quel est le problème avec l'extrait suivant?
#include <tr1/functional>
#include <functional>
#include <iostream>
using namespace std::tr1::placeholders;
struct abc
{
typedef void result_type;
void hello(int)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void hello(int) const
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
abc()
{}
};
int
main(int argc, char *argv[])
{
const abc x;
int a = 1;
std::tr1::bind(&abc::hello, x , _1)(a);
return 0;
}
En essayant de le compiler avec g ++ - 4.3, il semble que les fonctions surchargées de l’qualificateur cv confondent à la fois tr1::mem_fn<>
et tr1::bind<>
. L’erreur suivante apparaît:
no matching function for call to ‘bind(<unresolved overloaded function type>,...
Au lieu de cela, l'extrait de code suivant est compilé mais semble rompre la const-correctness :
struct abc
{
typedef void result_type;
void operator()(int)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void operator()(int) const
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
abc()
{}
};
...
const abc x;
int a = 1;
std::tr1::bind( x , _1)(a);
Avez-vous un indice?
La solution
La recherche est effectuée à un moment où la constance de this
n'est pas connue. Vous devez juste lui donner un indice via le casting. Essayez ceci:
typedef void (abc::*fptr)(int) const; // or remove const
std::tr1::bind((fptr)&abc::hello, x , _1)(a);
Vous remarquerez peut-être également ici que la suppression de const
fonctionne toujours. En effet, vous devez passer x par le pointeur (car le premier argument d'une fonction membre C ++, le paramètre implicite &
, est toujours un pointeur). Essayez ceci à la place:
typedef void (abc::*fptr)(int) const; // won't compile without const (good!)
std::tr1::bind((fptr)&abc::hello, &x , _1)(a);
Comme cela a été découvert au cours de mes commentaires ci-dessous, si vous omettez le bind
comme à l'origine, vous transmettez x par valeur , ce qui n'est généralement pas ce que vous voulez (bien que petite différence pratique dans votre exemple particulier). Cela ressemble en réalité à un piège malheureux pour <=>.
Autres conseils
On a répondu à cette question, mais je trouve que le meilleur moyen de spécifier une surcharge avec bind est de le spécifier sur le modèle:
std::tr1::bind<void(foo::*)(int)>(&foo::bar);
Cette méthode est tout aussi explicite, mais plus courte que le casting (avec static_cast
de toute façon. Mais elle est plus propre que le C-cast, qui a la même longueur.
Comme John l'a suggéré, les problèmes soulevés dans ces extraits sont les suivants:
- Lorsque vous passez un pointeur de fonction membre , vous devez spécifier sa signature (en cas de surcharge)
-
bind()
sont passés arguments par valeur.
Le premier problème est résolu en projetant le pointeur de fonction membre fourni pour lier:
std::tr1::bind(static_cast< void(abc::*)(int) const >(&abc::hello), x, _1)(a);
La seconde peut être résolue en passant l'objet appelable par adresse (comme suggéré par John) ou par TR1 reference_wrapper<>
- sinon, il sera passé par valeur, ce qui rend la hall-rupture brisant
Donné x un objet appelable:
std::tr1::bind( std::tr1::ref(x) , _1)(a);
a
transmettra operator()
au bon <=> conformément à la x constness .