Wie ordne ich einem Funktionsnamen in C ++ einen Alias ​​zu?

100

Es ist einfach, einen neuen Namen für einen Typ, eine Variable oder einen Namespace zu erstellen. Aber wie ordne ich einer Funktion einen neuen Namen zu? Zum Beispiel möchte ich den Namen hollerfür verwenden printf. #define ist offensichtlich ... auf andere Weise?

Lösungen:

  1. #define holler printf
  2. void (*p)() = fn; //function pointer
  3. void (&r)() = fn; //function reference
  4. inline void g(){ f(); }
Agnel Kurian
quelle
Vielen Dank an euch alle. Meine Kollegen werden es lieben, es void (&NewName)(some_vector&, float, float, float, float) = OldName;bei meinem nächsten Check-in zu sehen.
Agnel Kurian
18
nicht so sehr, wie sie es lieben werden, wenn Sie zufällige Namen für Standardbibliotheksfunktionen verwenden.
Jalf
2
Ich spiele printfhier nicht mit . Das war nur ein Beispiel. Das Problem hier hat mehr mit Einschränkungen des Englischen als mit irgendetwas anderem zu tun. Ich habe eine einzige Funktion, die Zweck A und Zweck B erfüllt, aber ich kann hier einfach keinen einzigen Namen finden, der beiden Zwecken dient.
Agnel Kurian
2
@Neil, genau. T &a = b;erstellt einen neuen Namen für b. typedeffür Typen und namespace A=B;für Namespaces.
Agnel Kurian
2
Es gibt using BaseClass::BaseClassMethodund es gibt using AliasType = Type;und es gibt sogar namespace AliasNamespace = Namespace;. Was wir vermissen istusing AliasFunction = Function;
anton_rh

Antworten:

114

Es gibt verschiedene Ansätze:

  • Mit C ++ 11 mit nicht vorlagenübergreifenden Funktionen können Sie einfach Folgendes verwenden:

    const auto& new_fn_name = old_fn_name;
  • Wenn diese Funktion mehrere Überladungen aufweist, sollten Sie Folgendes verwenden static_cast:

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

    Beispiel: Es gibt zwei Funktionsüberladungen std::stoi

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

    Wenn Sie einen Alias ​​für die erste Version erstellen möchten, sollten Sie Folgendes verwenden:

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

    Hinweis: Es gibt keine Möglichkeit, einen Alias ​​für überladene Funktionen so zu erstellen, dass alle überladenen Versionen funktionieren. Sie sollten daher immer genau angeben, welche Funktionsüberladung Sie möchten.

  • Mit C ++ 14 können Sie mit constexprVorlagenvariablen noch weiter gehen . Damit können Sie Vorlagenfunktionen als Alias ​​verwenden:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
  • Darüber hinaus haben Sie ab C ++ 11 eine Funktion namens std::mem_fn, die es ermöglicht, Mitgliedsfunktionen zu aliasen. Siehe folgendes Beispiel:

    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
quelle
1
Ausgezeichnet, wie wäre es mit C ++ 98? Ich habe eine Klasse mit 2 "Reset" -Überladungen, die zum Setzen und Zurücksetzen verwendet werden. Intern kein Problem. Für externe Benutzer wollte ich den Alias ​​"set" verwenden, damit er für den Kontext intuitiv ist (ein standardmäßig erstelltes, clear () 'd usw .; Arbeitsobjekt zurücksetzen). Klassenmethoden: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "Zurücksetzen" mit entsprechenden Signaturen erledigen die Arbeit. Da ich die Signatur in der Klassendeklaration habe, kann ich einfach die Klasse initialisieren: set (reset), set (reset). Wenn nicht, funktioniert Ihr explizites static_cast-Beispiel?
Luv2code
8
Es scheint ein Problem mit dem constexprAnsatz der Vorlagenvariablen zu geben: Der Alias ​​kann keinen Typabzug durchführen. Der Compiler verlangt von mir die Bereitstellung einer Vorlagenparameterliste (ich schreibe eine variable Vorlagenfunktion): Ich kann ohne eine Vorlagenargumentliste nicht auf die Variablenvorlage "alias_to_old" verweisen
user69818
1
constexpr auto new_fn_name = old_fn_namefunktioniert in C ++ 11 (zumindest in gcc 4.9.2) und ist besser als das Platzieren &. Der Aufruf muss nicht immer über den Zeiger erfolgen, sodass die Funktion anstelle des Aufrufs eingefügt werden kann.
Nur am
Mit C ++ 14 generischen Lambdas konnte ich Folgendes tun, was auch funktionieren sollte, wenn die Zielfunktion mehrere Überladungen aufweist: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall
1
Verwenden std::mem_fnist kein Alias, da es viel mehr Magie hinter dem Sinn ausübt.
cgsdfc
34

Sie können einen Funktionszeiger oder eine Funktionsreferenz erstellen:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference
Brian R. Bondy
quelle
2
Das nimmt den Kuchen. Ich wusste nichts über Funktionsreferenzen.
Agnel Kurian
@Vulcan: Sie sind fast gleich, da Sie beide mit derselben Syntax aufrufen können, aber ihre Adressen unterscheiden sich ein wenig. r belegt keinen eigenen Speicherplatz mit einer Adresse.
Brian R. Bondy
1
Wie würden Sie fnmit dem Alias anrufen ? Können Sie den Funktionszeiger und die Funktionsreferenz erklären? Wie unterscheiden sie sich? Sind sie hier gleich?
ma11hew28
1
@Matt, du nennst es genau so, wie du fn nennen würdest. r();
Agnel Kurian
Wie würden Sie dies für eine Instanzmethode tun? EDIT: Dies scheint zu kompilieren:void (&r)() = this->fn;
Sam
21
typedef int (*printf_alias)(const char*, ...);
printf_alias holler = std::printf;

Sollte es dir gut gehen.

jer
quelle
Befindet sich printf nicht im globalen Namespace?
Agnel Kurian
3
Es ist global, wenn Sie <stdio.h> enthalten, aber in std, wenn Sie <cstdio> enthalten haben
Injektilo
@einpoklum: Es ist nichts falsch mit decltype , aber die Antwort stammt aus dem Jahr 2010. Damals gab es nicht so, decltypewie es in c ++ 11 eingeführt wurde. Darüber hinaus sollte dies auch mit der guten alten Ebene C
funktionieren
10

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

MSalters
quelle
7

Verwenden Sie einen Inline-Wrapper. Sie erhalten beide APIs, behalten jedoch die einzelne Implementierung bei.

John
quelle
3

Von 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)...); \
}
Segelfisch009
quelle
0

Es ist erwähnenswert, dass IMO zwar die ursprüngliche Frage (und gute Antworten) auf jeden Fall nützlich sind, wenn Sie eine Funktion umbenennen möchten (es gibt gute Gründe dafür!), Wenn Sie jedoch nur einen tiefen Namespace entfernen möchten, aber Behalte den Namen, dafür gibt es das usingSchlüsselwort:

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
}

Dies hat auch den Vorteil, dass es auf einen Bereich beschränkt ist, obwohl Sie es immer auf der obersten Ebene einer Datei verwenden können. Ich benutze dies oft für coutund endldeshalb muss ich nicht ALLES stdmit dem Klassiker using namespace std;oben in einer Datei einfügen, aber auch nützlich, wenn Sie so etwas wie std::this_thread::sleep_for()viel in einer Datei oder Funktion verwenden, aber nicht überall und keine anderen Funktionen aus dem Namespace. Wie immer wird davon abgeraten, es in .h-Dateien zu verwenden, da sonst der globale Namespace verschmutzt wird.

Dies ist nicht dasselbe wie das obige "Umbenennen", aber es ist oft das, was wirklich gewünscht wird.

Kevin Anderson
quelle
0

Mit generischen C ++ 14-Lambdas konnte ich Folgendes tun, was auch funktionieren sollte, wenn die Zielfunktion mehrere Überladungen aufweist:

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };
Anthony Hall
quelle