Comment attribuer un alias à un nom de fonction en C ++?

100

Il est facile de créer un nouveau nom pour un type, une variable ou un espace de noms. Mais comment attribuer un nouveau nom à une fonction? Par exemple, je souhaite utiliser le nom hollerde printf. #define est évident ... d'une autre manière?

Solutions:

  1. #define holler printf
  2. void (*p)() = fn; //function pointer
  3. void (&r)() = fn; //function reference
  4. inline void g(){ f(); }
Agnel Kurian
la source
Merci à tous. Mes collègues vont adorer le voir void (&NewName)(some_vector&, float, float, float, float) = OldName;lors de mon prochain enregistrement.
Agnel Kurian
19
pas autant qu'ils aimeront vous voir utiliser des noms aléatoires pour les fonctions de bibliothèque standard.
jalf
2
Je ne joue pas avec printfici. Ce n'était qu'un exemple. Le problème ici a plus à voir avec les limitations de l'anglais qu'autre chose. J'ai une seule fonction au service du but A et du but B, mais je suis tout simplement incapable de trouver un seul nom servant les deux buts ici.
Agnel Kurian
2
@Neil, précisément. T &a = b;crée un nouveau nom pour b. typedefpour les types et namespace A=B;pour les espaces de noms.
Agnel Kurian
2
Il y a using BaseClass::BaseClassMethod, et il y a using AliasType = Type;, et il y a même namespace AliasNamespace = Namespace;. Ce qu'il nous manque, c'estusing AliasFunction = Function;
anton_rh

Réponses:

114

Il existe différentes approches:

  • Avec C ++ 11 avec des fonctions non surchargées non-template, vous pouvez simplement utiliser:

    const auto& new_fn_name = old_fn_name;
  • Si cette fonction a plusieurs surcharges, vous devez utiliser static_cast:

    const auto& new_fn_name = static_cast<OVERLOADED_FN_TYPE>(old_fn_name);

    Exemple: il y a deux surcharges de fonction std::stoi

    int stoi (const string&, size_t*, int);
    int stoi (const wstring&, size_t*, int);

    Si vous souhaitez créer un alias vers la première version, vous devez utiliser ce qui suit:

    const auto& new_fn_name = static_cast<int(*)(const string&, size_t*, int)>(std::stoi);

    Remarque: il n'y a aucun moyen de créer un alias pour une fonction surchargée de sorte que toutes ses versions surchargées fonctionnent, vous devez donc toujours spécifier la surcharge de fonction exacte que vous souhaitez.

  • Avec C ++ 14, vous pouvez aller encore plus loin avec constexprles variables de modèle. Cela vous permet d'aliaser des fonctions basées sur des modèles:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
  • De plus, à partir de C ++ 11, vous avez une fonction appelée std::mem_fnqui permet d'aliaser les fonctions membres. Consultez l'exemple suivant:

    struct A {
       void f(int i) {
          std::cout << "Argument: " << i << '\n';
       }
    };
    
    
    A a;
    
    auto greet = std::mem_fn(&A::f); // alias to member function
    // prints "Argument: 5"
    greet(a, 5); // you should provide an object each time you use this alias
    
    // if you want to bind an object permanently use `std::bind`
    greet_a = std::bind(greet, a, std::placeholders::_1);
    greet_a(3); // equivalent to greet(a, 3) => a.f(3);
sasha.sochka
la source
1
Excellent, qu'en est-il du C ++ 98? J'ai une classe avec 2 surcharges de «réinitialisation» utilisées pour définir et réinitialiser. En interne, pas de problème. Pour les utilisateurs externes, je voulais alias comme "set" donc c'est intuitif pour le contexte (définir un objet de travail par défaut, clear () 'd etc .; réinitialiser l'objet de travail). Méthodes de classe: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "reset" w / signatures correspondantes font le travail. Puisque j'ai la signature dans la déclaration de classe, puis-je simplement initialiser la classe,: set (reset), set (reset). Sinon, votre exemple explicite de static_cast fonctionnera-t-il?
Luv2code
8
Il semble y avoir un problème avec l' constexprapproche des variables de modèle: l'alias ne peut pas faire de déduction de type. Le compilateur me demande de fournir une liste de paramètres de modèle (j'écris une fonction de modèle variadique): ne peut pas faire référence au modèle de variable `alias_to_old 'sans liste d'arguments de modèle
user69818
1
constexpr auto new_fn_name = old_fn_namefonctionne en C ++ 11 (au moins dans gcc 4.9.2) et vaut mieux que placer &. Il ne nécessite pas que l'appel soit toujours effectué via un pointeur et permet ainsi à la fonction d'être intégrée à la place de l'appel.
ony
Avec les lambdas génériques C ++ 14, j'ai pu faire ce qui suit, qui devrait également fonctionner lorsque la fonction cible a plusieurs surcharges: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall
1
L'utilisation std::mem_fnn'est pas un alias car elle fait beaucoup plus de magie derrière le sens.
cgsdfc
35

Vous pouvez créer un pointeur de fonction ou une référence de fonction:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference
Brian R. Bondy
la source
2
Cela prend le gâteau. Je ne connaissais pas les références de fonction.
Agnel Kurian
@Vulcan: Ils sont presque les mêmes en ce sens que vous pouvez les appeler tous les deux avec la même syntaxe, mais leurs adresses sont un peu différentes. r n'occupe pas son propre espace mémoire contenant une adresse.
Brian R. Bondy
1
Comment appelleriez-vous fn, en utilisant l'alias? Pouvez-vous expliquer le pointeur de fonction et la référence de la fonction? Comment sont-ils différents? Sont-ils les mêmes ici?
ma11hew28
1
@Matt, vous l'appelez exactement comme vous appelleriez fn. r();
Agnel Kurian
comment feriez-vous cela pour une méthode d'instance? EDIT: cela semble compiler:void (&r)() = this->fn;
Sam
21
typedef int (*printf_alias)(const char*, ...);
printf_alias holler = std::printf;

Ça devrait vous convenir.

secousse
la source
Printf n'est-il pas dans l'espace de noms global?
Agnel Kurian
3
c'est global si vous avez inclus <stdio.h>, mais dans std si vous avez inclus <cstdio>
Injektilo
@einpoklum: Il n'y a rien de mal avec decltype , mais la réponse date de 2010. À l'époque, il n'existait pas decltypecomme il a été introduit dans c ++ 11. De plus, cela devrait également fonctionner avec la bonne vieille plaine C.
Phidelux
10

int (*holler)(const char*, ...) = std::printf;

MSalters
la source
7

Utilisez un wrapper en ligne. Vous obtenez les deux API, mais gardez l'implémentation unique.

John
la source
3

Depuis fluentcpp : ALIAS_TEMPLATE_FUNCTION (f, g)

#define ALIAS_TEMPLATE_FUNCTION(highLevelF, lowLevelF) \
template<typename... Args> \
inline auto highLevelF(Args&&... args) -> decltype(lowLevelF(std::forward<Args>(args)...)) \
{ \
    return lowLevelF(std::forward<Args>(args)...); \
}
voilier009
la source
1

Avec les lambdas génériques C ++ 14, j'ai pu effectuer les opérations suivantes, ce qui devrait également fonctionner lorsque la fonction cible a plusieurs surcharges:

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };
Anthony Hall
la source
Hah! Cela me rend triste, même @ user5534993 qui vous a initialement poussé à soumettre cette réponse, puis n'a pas pu lancer un vote favorable. Eh bien, ici, en ai un sur moi.
Ferd
0

Il convient de mentionner ici IMO que, bien que la question originale (et les bonnes réponses) soient certainement utiles si vous souhaitez renommer une fonction (il y a de bonnes raisons de le faire!), Si tout ce que vous voulez faire est de supprimer un espace de noms profond mais gardez le nom, il y a le usingmot - clé pour cela:

namespace deep {
  namespace naming {
    namespace convention {
      void myFunction(int a, char b) {}
    }
  }
}
int main(void){
  // A pain to write it all out every time
  deep::naming::convention::myFunction(5, 'c');

  // Using keyword can be done this way
  using deep::naming::convention::myFunction;
  myFunction(5, 'c');  // Same as above
}

Cela a également l'avantage d'être confiné à une étendue, bien que vous puissiez toujours l'utiliser au niveau supérieur d'un fichier. J'utilise souvent ceci pour coutet endldonc je n'ai pas besoin de mettre TOUT stdavec le classique using namespace std;en haut d'un fichier, mais aussi utile si vous utilisez quelque chose comme std::this_thread::sleep_for()beaucoup dans un fichier ou une fonction, mais pas partout, et pas d'autres fonctions de l'espace de noms. Comme toujours, il est déconseillé de l'utiliser dans des fichiers .h, sinon vous polluerez l'espace de noms global.

Ce n'est pas la même chose que le "renommage" ci-dessus, mais c'est souvent ce que l'on souhaite vraiment.

Kevin Anderson
la source