2017-10-07 10 views
1

Est-il possible de stocker une fonction qui a un comportement similaire à la fonction suivante:fonctions de stockage C++ que l'attaquant références universelles

void target(int foo, size_t bar) {} 
void target(std::string foo, int bar) {} 

template<T...> 
void forwarding_func(T&&... args) 
{ 
    target(std::forward<T>(args)...); 
} 

auto forwarding_callable = ? 

Comment peut-on construire l'objet appelable pour les types T qui a le même comportement que la forwarding_func. Je dois le stocker pour une utilisation ultérieure, j'ai donc besoin d'un objet std :: function? Est-il possible de stocker même des lambdas comme ceux-ci dans les objets de fonction?

auto f = [](auto&& x){ 
     myfunction(std::forward<decltype(x)>(x)); 
} 
+0

oui, il est possible d'utiliser lambdas. Si vous voulez les stocker, vous pouvez utiliser 'std :: function' ou une classe, modélisée sur le type d'un objet lambda passé. –

+1

Il est possible de stocker une telle chose mais * not * dans un objet std :: function. –

+0

S'il vous plaît noter, que votre exemple lambda (si cela a fonctionné) ne prend pas une référence universelle, mais une référence rvalue, c'est à dire. il n'accepterait que des valeurs comme argumentes. Je sais que vous demandez explicitement comment faire cela correctement, mais il me semble encore qu'il y a une certaine confusion là-bas. Vous pouvez lire l'article de Scott Meyer (https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers) sur la différence entre les références universelles et les références rvalue. – Knoep

Répondre

2

Vous avez besoin d'un objet de fonction, qui est, avec un operator(). Cet opérateur doit être un modèle et avoir la même signature (ou similaire) que forwarding_func. Vous pouvez en créer un de différentes façons.

La méthode la plus simple consiste à utiliser le lambda générique C++ 17. Si vous ne pouvez pas utiliser, vous pouvez définir votre propre:

struct forwarder { 
    template <typename ... Args> 
     void operator()(Args&& ... args) { ... etc } 
}; 

Il y a une manière complètement différente de créer de tels porteurs. Il existe une proposition pour ajouter une fonction overload à la bibliothèque standard. Vous l'utiliser comme ceci:

auto callable = std::overload (
    (void (*)(int, size_t))(target), 
    (void (*)(std::string, int))(target) 
); 

Compilateurs ne mettent pas en œuvre std :: surcharge car il ne fait pas partie d'une norme encore, mais vous pouvez facilement mettre en œuvre vous-même ou trouver une mise en œuvre sur le net (voir this question par exemple).

0

références universelles et std::forward n'a de sens que dans les modèles de fonction, comme dans ce cas, vous ne savez pas si votre argument est une lvalue référence ou non. Si c'est le cas, vous voulez le transmettre tel quel, sinon, vous voulez le transférer dans la fonction que vous transférez. C'est exactement ce que fait std::foward. Here est une très bonne introduction aux références rvalue et à la transmission parfaite. Les fonctions non-template spécifient si elles prennent leurs arguments par valeur ou par référence (lvalue-), vous pouvez simplement les déplacer ou ne pas les déplacer en conséquence et vous n'avez pas besoin de std::foward.

Étant donné que les modèles de fonction ne fonctionnent pas eux-mêmes, vous ne pouvez pas les stocker dans un lambda. Vous pouvez toutefois créer un foncteur avec un operator() basé sur un modèle:

struct Forwarding_Functor 
{ 
    template<class... T> 
    void operator()(T&&... args) 
    { 
     target(std::forward<T>(args)...); 
    } 
};