12
votes

Comment surcharger des constructeurs sur la signature d'une STD :: Fonction?

J'essaie d'écrire une classe avec des constructeurs surchargés qui acceptent STD :: Fonction Objets en tant que paramètres, mais bien sûr chaque chose em> peut être implicitement lancé à une marque STD :: Fonction de toute signature . Ce qui est vraiment utile, naturellement.

Exemple: P>

class Foo {
  Foo( std::function<void()> fn ) {
    ...code...
  }
  Foo( std::function<int()> fn ) {
    ...code...
  }
};

Foo a( []() -> void { return; } );    // Calls first constructor
Foo b( []() -> int { return 1; } );   // Calls second constructor


2 commentaires

Est-ce le cas que vous vous attendez à ce que l'argument du constructeur soit quelque chose d'appelable sans paramètres (bien que avec un type de retour arbitraire)? Ou attendez-vous une signature (auquel cas je me demande ce que le constructeur peut faire du tout avec une telle chose)?


@LUC: Je m'attends à une signature spécifique - ou de signatures. Par exemple, je souhaite peut-être une surcharge acceptant une fonction , une acceptation d'une fonction et une fonction d'acceptation d'une fonction < int (int)> . Dans ce cas particulier, je n'ai besoin que des deux premiers; Mais le principe est le même. C'est très simple: Je veux surcharger sur le type d'une fonction <> paramètre . Cela ne devrait pas être une attente déraisonnable. Si je peux surcharger sur d'autres choses (INTS, les pointeurs de fonction brute), il devrait être un moyen de dire "Je m'attends à une fonction <> objet avec une signature spécifique .


3 Réponses :


2
votes

Il existe donc de nombreuses façons de l'approcher, ce qui prend diverses quantités de travail. Aucun d'entre eux ne sont complètement triviaux.

Premièrement, vous pouvez déballer les signatures de types de types passés en examinant T :: opérateur () ou vérifiant si elle est de type r (* ) (Args ...) code>. P>

puis vérifiez l'équivalence de signature. P>

Une seconde approche consiste à détecter la compatibilité des appels. Écrivez une classe de traits comme ceci: p> xxx pré>

qui est soit std :: true_type code> ou std :: faux_type code> en fonction de Si DeclType () (déclval () ...) code> est convertible à la valeur code> de la signature code>. Dans ce cas, cela résoudrait votre problème. P>

Maintenant, plus de travail doit être effectué si les deux signatures que vous surchargez sont compatibles. IE, imaginez si vous avez un std :: Fonction code> et std :: Fonction code> - ils sont compatibles croisées . P>

Pour déterminer quel est le "meilleur", vous pouvez regarder par-dessus ici Lors d'une question précédente de la mine, où nous pouvons prendre un tas de signatures et trouver qui correspond le mieux. Ensuite, faites une vérification de type de retour. Cela devient complexe! P>

si nous utilisons la solution Call_Compatible CODE>, ce que vous finissez par faire est la suivante: p>

template<typename Sig, typename F, typename=void>
struct call_compatible:std::false_type {};

template<typename R, typename...Args, typename F>
struct call_compatible<R(Args...), F, typename std::enable_if<
  std::is_convertible<
    decltype(
      std::declval<F&>()( std::declval<Args>()... )
    )
    , R
  >::value
>::type>:std::true_type {};


12 commentaires

Je ne sais pas comparer le type de t :: opérateur () avec Void * (int). J'ai essayé modèle foo (tyname acier_if_if :: valeur, t> :: type fn) 'T Travailler. Je vais regarder votre deuxième suggestion.


Avec la deuxième suggestion, je reçois des erreurs étranges comme "type n'est pas membre de l'espace de noms global" etc. Je ne pense pas que Visual Studio 2012 prend en charge les modèles variadiques. Soupir.


@Sodlamesty Oh, vous utilisez VS2012? V2 ne fonctionnera pas à moins que vous fassiez un piratage sérieux, car la prise en charge de VS2012 pour Sfinae basée sur DeclType est anémique. Essayez V1 - Le type de T :: opérateur (Opérateur () est un pointeur de fonction de membre non un pointeur de fonction ( r (T :: *) (args ...) En général, et quelque chose comme void (T :: *) () spécifiquement). J'écrirais une classe de traits qui dit "est nullaire et retourne vide " et "est annulé et renvoie int " et essayez-les, puis essayez de activer_if. sfinae avec eux.


J'ai bien peur que je n'ai toujours pas de problèmes. J'ai le code suivant: Classe FOO {Modèle FOO (Typename Activer_if :: Valeur, T> :: TYPE FN) {} Modèle FOO (Typename Activer_if :: valeur, t> :: Type fn) {}}; Fonction fn1 = [] (int n) {retour; }; Foo a (fn1); et j'ai des erreurs telles que: "Erreur C2535: 'FOO :: FOO (FOO (FOO (FOO (FOO (Type de type T> :: Type)': Fonction Membre déjà définie ou déclarée" et "C2664:" FOO :: FOO ( const foo &) ': impossible de convertir le paramètre 1 de' std :: fonction <_fty> 'to' const foo & '"


Aussi "AVERTISSEMENT C4346: 'T: :()': Nom de la dépendance n'est pas un type" et "IntelliSense: Aucune instance de constructeur" FOO :: FOO "correspond à la liste des arguments Les types d'arguments sont: (STD :: Fonction ) ". Où est-ce que je vais mal?


Si vous descendez cette route (examinez t :: opérateur (opérateur () gasp sacrilège!) Gardez à l'esprit que vous dites non à aucun ou tous les objets de fonction polymorphes.


@R: Je ne suis pas sûr de ce que vous entendez par là. Un objet définissant sûrement un opérateur () de la bonne signature serait accepté? Au moins, si je peux avoir la fichue chose à travailler, ce qui n'a pas l'air très probable.


@SOD N ° comme je l'ai dit, des objets de fonction polymorphes qui s'appuient sur cette signature ne seront pas acceptés.


@R: Je ne comprends pas. Je ne suis même pas sûr de ce qu'est un "objet de fonction polymorphique". Je sais quels moyens polymorphes et je sais quel objet de fonction est. Voulez-vous dire une fonction qui prend un type plus dérivé et renvoie un type moins dérivé ne parviendra pas à correspondre? Si oui, c'est toujours mieux que ce que j'ai maintenant, qui est une erreur de compilation.


Ah, vous auriez pu demander quel objet de fonction polymorphe est. Deux exemples: struct One_polymorphic_CALLABLE {Modèle Opérateur vide () (t); }; structurer autre_polymorphic_callable {Opérateur vide de modèle () (int); opérateur vide () (std :: vecteur ); }; . Ils peuvent être appelés avec différentes formes et sont donc polymorphes. Inspection T :: opérateur () ne fonctionnera pas car il n'y a pas un T :: opérateur () pour inspecter.


@R: Ah, je vois. En tout état de cause, il manque une solution parfaite, une solution imparfaite bat une erreur de compilation. Ce que, comme je le dis, est ce que j'ai pour le moment jusqu'à ce que je puisse en quelque sorte disambiguez ces deux constructeurs.


@Sodlamesty quand je reçois ensuite un peu de temps, je vais essayer de travailler à elle - mais dans votre cas étroit particulier, tout ce que vous voulez faire est de détecter le type de retour de votre opérateur () et de détecter S'il est void ou int . Il s'agit d'un problème plus étroit qui nécessitera une picothérapie de jiggy fantaisie impliquant des fonctions de pointes-à-membres et des fonctions de modèle et de déduction de type dans MSVC2012 en raison de leur support Mediocre C ++ 11 à ce jour.



5
votes

Voici quelques scénarios courants et pourquoi je ne pense pas std :: fonction code> est approprié pour eux:

class Foo {
public:
    // assuming is_callable<F, int()> is a subset of
    // is_callable<F, void()>
    template<typename Functor,
             Requires<is_callable<Functor, void()>>...>
    Foo(Functor f)
        : Foo(std::move(f), select_overload {})
    {}

private:
    // assuming choice<0> is preferred over choice<1> by
    // overload resolution

    template<typename Functor,
             EnableIf<is_callable<Functor, int()>>...>
    Foo(Functor f, choice<0>);
    template<typename Functor,
             EnableIf<is_callable<Functor, void()>>...>
    Foo(Functor f, choice<1>);
};


6 commentaires

Je veux savoir où tous ces commentaires sont allés.


Vous pourrait demander à Meta. Mais c'est une sorte de FAQ là-bas. Probablement ils ont été nettoyés par un mod. Le plus souvent, cela ne se produit que lorsque l'attention a été attirée sur une discussion de commentaire (prolongée), car un message individuel a été signalé pour l'attention du modérateur. Voir Overflow Meta Stack pour Justification (Si une telle chose existe vraiment - préparez-vous à être en désaccord, comme la plupart d'entre nous)


Je vois. Donc, si quelqu'un a ajouté des commentaires avant cela ne s'est passé, je ne les ai pas vus. Donc, je ne sais pas si quelqu'un essaie toujours d'aider ou non. Comme les choses se tiennent, je n'ai toujours aucune solution.


@Sodlamesty, j'ai offert une correction de votre dernier code. Principalement Vous manquiez certains < Code> Typename .


@LUC: Pourriez-vous me relier à la correction, s'il vous plaît? Le mod de la gâchette-heureux l'a supprimé avant que je l'ai repéré.


@Sodlamesty je ne l'ai pas à portée de main.



0
votes

J'ai donc une solution complètement nouvelle à ce problème qui fonctionne dans MSVC 2013 et ne suce pas (comme regarder un pointeur sur opérateur () code>).

Tag IT. p>

une étiquette pouvant transporter n'importe quel type: p> xxx pré>

une fonction qui prend une expression de type et génère une étiquette de type: p>

template<class T> struct tag{
  using type=T;
  tag(tag const&)=default;
  tag()=default;
  template<class U,
    class=typename std::enable_if<std::is_convertible<U,T>::value>::type
  >
  tag( tag<U> ){}
};


0 commentaires