2017-02-09 2 views
-2

Cela peut être une question naïve, mais ici, il va de toute façon:L'utilisation correcte d'un foncteur dans std :: for_each et std :: transformer

Supposons que j'ai une classe comme ceci:

class func { 
    public: 
     int operator()(int it) const { return it + 21; } 
}; 

Comme son clair, un foncteur est défini dans la classe ci-dessus, et si je fais ceci:

func obj; 
int capture = obj(9); 
cout << capture << endl; 

le résultat sera 30, comme une évidence. Mais supposons que j'utilise std::transform pour transformer un conteneur en utilisant les valeurs d'un autre conteneur selon le foncteur ci-dessus défini.

vector<int> v, vi; 
v.push_back(1); 
v.push_back(2); 
vi.resize(v.size()); 

Je suivre la syntaxe ci-dessous, où j'invoquer le foncteur directement en utilisant le nom de la classe, et aussi, aucun argument est passé à foncteur (dont il a besoin en fonction de la définition):

std::transform(v.begin(), v.end(), vi.begin(), func()); 

Cela fonctionne parfaitement. Pourquoi cela arrive-t-il ? Malgré ne pas utiliser une instance du func et aussi, sans passer un argument (qui est l'élément du premier conteneur apparemment), pourquoi cela fonctionne?

De plus, si j'utilise une instance de func, avec un argument comme je l'ai fait ci-dessus, cela provoque une erreur de compilation.

func instance; 
std::transform(v.begin(), v.end(), vi.begin(), instance()); 

Comment utiliser un foncteur correctement dans std::transform/std::for_each? Pourquoi y a-t-il une différence dans la façon dont la méthode functor est invoquée?

En outre, à partir this answer on functors, nous avons le morceau de code suivant:

// this is a functor 
struct add_x { 
    add_x(int x) : x(x) {} 
    int operator()(int y) const { return x + y; } 

    private: 
    int x; 
}; 

std::vector<int> in; // assume this contains a bunch of values) 
std::vector<int> out(in.size()); 
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence 
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 

La réponse précise que add_x(1) agit un foncteur ici (et non une instance), comment est-il un exemple dans l'exemple ci-dessus, je a donné?

+1

Ceci est un exemple: 'lambda_function()'. – juanchopanza

+0

Si c'est une instance, comment fonctionne-t-elle en tant que foncteur, sans argument? Ne devrait-il pas lancer une erreur de compilation? – Jarvis

+0

Pourquoi appelez-vous les choses "lambda_function" et "capture" lorsque votre code n'a rien à voir avec les fonctions lambda et les captures? –

Répondre

4
std::transform(v.begin(), v.end(), vi.begin(), lambda_function()); 
//            ^^^^^^^^^^^^^^^^^ 

Ce n'invoque lambda_function::operator(), il crée simplement une instance temporaire de lambda_function. À l'intérieur std::transform, cet objet aura son operator() appelé itérativement avec le contenu de v comme arguments.

Il est plus évident ce qui se passe si vous utilisez 11 les années de C calées initialisation:

std::transform(v.begin(), v.end(), vi.begin(), lambda_function{}); 

Ou peut-être si vous considérez ceci:

lambda_function()(0); 
//    ^^ creates instance 
//    ^^^ calls operator() 

En ce qui concerne votre deuxième exemple:

std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 
//            ^^^^^^^^ 

Cela crée à nouveau une instance temporaire de add_x, mais au lieu d'appeler le constructeur par défaut, il appelle le constructeur add_x(int x);. Ceci initialise un état dans l'objet fonction de sorte que lorsque operator() est appelé à l'intérieur de std::transform, il ajoute le nombre donné.

+0

S'il n'invoque pas le foncteur, comment prend-il chaque élément et y ajoute-t-il 21? – Jarvis

+1

L'objet fonction est appelé dans l'algorithme. – TartanLlama

+0

S'il vous plaît voir la modification j'ai ajouté ci-dessus. – Jarvis

3

Dans ce,

std::transform(v.begin(), v.end(), vi.begin(), lambda_function()); 

lambda_function() default-crée une instance temporaire de lambda_function.
lambda_function est une classe, pas un objet.
lambda_function() est un objet.

Dans

lambda_function instance; 
std::transform(v.begin(), v.end(), vi.begin(), instance()); 

vous n'êtes pas passer l'instance, vous essayez d'appeler un operator() sans argument.
instance est un objet, pas une classe.

Pour passer l'instance, écrivez

lambda_function instance; 
std::transform(v.begin(), v.end(), vi.begin(), instance); 
+0

S'il vous plaît voir la modification que j'ai ajouté ci-dessus. – Jarvis

+0

@Jarvis Voyez si mon commentaire sous la question clarifie quelque chose. – molbdnilo

+0

En fait, pas complètement, comment 'add_x (1)' ajoute 1 à chaque élément du premier conteneur, alors que la définition de 'add_x (int y)' ajoute 'y' au membre de classe' x'? De plus, est-ce que 'add_x (1)' le constructeur appelle ou le foncteur appelle? – Jarvis

3

std::transform(InIt first, InIt last, OutIt dest, Fn f) en substance ce que cela:

while (first != last) { 
    *dest = f(*first); 
    first++; 
} 

Ainsi, lorsque vous appelez

std::transform(v.begin(), v.end(). vi.begin(), lambda_function()); 

vous créez un objet temporaire de type lambda_function et en passant cet objet à transform. À l'intérieur transform, cet objet est appelé sur chaque élément de la plage d'entrée, tout comme dans votre code int capture = obj(9);.

Pour utiliser un objet que vous avez déjà créé au lieu d'un laissez-passer temporaire, juste dans:

lambda_function instance; 
std::transform(v.begin(), v.end(), vi.begin(), instance); 

Notez que ce code ne pas () après instance; cela appelait operator() sans arguments, et lambda_function n'a pas de operator() qui peut être appelé sans arguments.

+0

S'il vous plaît voir la modification j'ai ajouté ci-dessus. – Jarvis

+1

@Jarvis - non, je ne vais pas essayer de suivre votre cible en mouvement. –

+0

Considère juste un petit ajout que j'ai fait, où il ne se comporte pas comme une instance, mais en tant que vrai foncteur, comment ça marche alors? – Jarvis

0

Voir la différence:

lambda_function obj; 
int capture = obj(9); 
cout << capture << endl; 

vs

int capture = lambda_function()(9); 
cout << capture << endl; 

et second exemple:

std::transform(v.begin(), v.end(). vi.begin(), lambda_function()); 

vs

lambda_function obj; 
std::transform(v.begin(), v.end(). vi.begin(), obj); 

dernier exemple:

std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 

vs

add_x obj(1); 
std::transform(in.begin(), in.end(), out.begin(), obj); 
+0

S'il vous plaît voir la modification j'ai ajouté ci-dessus. – Jarvis