Come si assegna un alias a un nome di funzione in C ++?


100

È facile creare un nuovo nome per un tipo, una variabile o uno spazio dei nomi. Ma come si assegna un nuovo nome a una funzione? Ad esempio, voglio usare il nome hollerper printf. #define è ovvio ... in qualche altro modo?

Soluzioni:

  1. #define holler printf
  2. void (*p)() = fn; //function pointer
  3. void (&r)() = fn; //function reference
  4. inline void g(){ f(); }

Grazie a tutti voi. I miei colleghi adoreranno vederlo void (&NewName)(some_vector&, float, float, float, float) = OldName;al mio prossimo check-in.
Agnel Kurian

19
non tanto quanto adoreranno vederti usare nomi casuali per le funzioni di libreria standard.
jalf

2
Non sto scherzando printfqui. Quello era solo un esempio. Il problema qui ha più a che fare con i limiti dell'inglese che con qualsiasi altra cosa. Ho un'unica funzione che serve allo scopo A e allo scopo B ma semplicemente non riesco a trovare un unico nome che serva a entrambi gli scopi qui.
Agnel Kurian

2
@ Neil, precisamente. T &a = b;crea un nuovo nome per b. typedefper i tipi e namespace A=B;per gli spazi dei nomi.
Agnel Kurian

2
C'è using BaseClass::BaseClassMethod, e c'è using AliasType = Type;, e c'è anche namespace AliasNamespace = Namespace;. Quello che ci manca èusing AliasFunction = Function;
anton_rh

Risposte:


114

Esistono diversi approcci:

  • Con C ++ 11 con funzioni non di sovraccarico non di modello puoi semplicemente usare:

    const auto& new_fn_name = old_fn_name;
  • Se questa funzione ha più sovraccarichi dovresti usare static_cast:

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

    Esempio: ci sono due sovraccarichi di funzione std::stoi

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

    Se vuoi creare un alias per la prima versione dovresti usare quanto segue:

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

    Nota: non c'è modo di creare un alias per la funzione sovraccaricata in modo tale che tutte le sue versioni sovraccaricate funzionino, quindi dovresti sempre specificare quale esatta funzione di sovraccarico desideri.

  • Con C ++ 14 puoi andare ancora oltre con le constexprvariabili del modello. Ciò ti consente di creare alias funzioni basate su modelli:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
  • Inoltre, a partire da C ++ 11 hai una funzione chiamata std::mem_fnche consente di creare alias funzioni membro. Vedi il seguente esempio:

    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);

1
Eccellente, che ne dici di C ++ 98? Ho una classe w / 2 "reset" sovraccarichi utilizzati per impostare e ripristinare. Internamente, nessun problema. Per gli utenti esterni volevo alias come "set" in modo che sia intuitivo per il contesto (imposta un clear () 'd costruito in modo predefinito, ecc .; ripristina l'oggetto di lavoro). Metodi di classe: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "reset" con le firme corrispondenti fanno il lavoro. Dato che ho la firma nella dichiarazione della classe, posso semplicemente inizializzare la classe,: set (reset), set (reset). In caso contrario, il tuo esempio static_cast esplicito funzionerà?
Luv2code

8
Sembra esserci un problema con l' constexprapproccio delle variabili del modello: l'alias non può eseguire la deduzione del tipo. Il compilatore mi richiede di fornire un elenco di parametri del modello (sto scrivendo una funzione di modello variadico): non posso fare riferimento al modello di variabile `alias_to_old 'senza un elenco di argomenti del modello
utente69818

1
constexpr auto new_fn_name = old_fn_namefunziona in C ++ 11 (almeno in gcc 4.9.2) ed è meglio che posizionare &. Non richiede che la chiamata venga sempre eseguita tramite il puntatore e quindi consente l'inline della funzione al posto della chiamata.
ony

Con i lambda generici C ++ 14, sono stato in grado di eseguire quanto segue, che dovrebbe funzionare anche quando la funzione di destinazione ha più sovraccarichi: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall

1
L'uso nonstd::mem_fn è un alias poiché esegue molta più magia dietro il senso.
cgsdfc

35

È possibile creare un puntatore a funzione o un riferimento a una funzione:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference

2
Questo prende la torta. Non sapevo di riferimenti a funzioni.
Agnel Kurian

@Vulcan: sono quasi uguali in quanto puoi chiamarli entrambi con la stessa sintassi, ma i loro indirizzi sono un po 'diff. r non occupa il proprio spazio di memoria tenendo un indirizzo.
Brian R. Bondy

1
Come chiameresti fn, usando l'alias? Puoi spiegare il puntatore alla funzione e il riferimento alla funzione? Come sono differenti? Sono la stessa cosa qui?
ma11hew28

1
@ Matt, lo chiami esattamente come chiameresti fn. r();
Agnel Kurian

come faresti questo per un metodo di istanza? EDIT: questo sembra compilare:void (&r)() = this->fn;
Sam

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

Dovresti andare bene.


Printf non è nello spazio dei nomi globale?
Agnel Kurian

3
è globale se hai incluso <stdio.h>, ma in std se hai incluso <cstdio>
Injektilo

@einpoklum: Non c'è niente di sbagliato in decltype , ma la risposta è del 2010. Allora non c'era decltypeperché era stato introdotto in c ++ 11. Inoltre questo dovrebbe funzionare anche con la buona vecchia pianura C.
Phidelux


7

Usa un wrapper in linea. Ottieni entrambe le API, ma mantieni la singola implementazione.


3

Da 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)...); \
}

1

Con i lambda generici C ++ 14, sono stato in grado di fare quanto segue, che dovrebbe funzionare anche quando la funzione di destinazione ha più sovraccarichi:

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

Ah! Questo mi rende triste, anche @ user5534993 che originariamente ti ha spinto a inviare questa risposta, quindi non ha potuto lanciare un voto a modo tuo. Bene, ecco, prendine uno con me.
FeRD il

0

Vale la pena menzionare qui IMO che mentre la domanda originale (e le ottime risposte) sono sicuramente utili se vuoi rinominare una funzione (ci sono buone ragioni per farlo!), Se tutto ciò che vuoi fare è rimuovere uno spazio dei nomi profondo ma mantieni il nome, c'è la usingparola chiave per questo:

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
}

Questo ha anche il vantaggio di essere limitato a un ambito, sebbene sia sempre possibile utilizzarlo al livello superiore di un file. Lo uso spesso per coute endlquindi non ho bisogno di portare TUTTO stdcon il classico using namespace std;all'inizio di un file, ma è anche utile se stai usando qualcosa comestd::this_thread::sleep_for() molto in un file o in una funzione, ma non ovunque, e non altre funzioni dallo spazio dei nomi. Come sempre, è sconsigliato usarlo nei file .h, altrimenti inquinerai lo spazio dei nomi globale.

Questo non è lo stesso del "rinominare" sopra, ma spesso è ciò che si desidera veramente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.