Jak przypisać alias do nazwy funkcji w C ++?

100

Utworzenie nowej nazwy dla typu, zmiennej lub przestrzeni nazw jest łatwe. Ale jak przypisać nową nazwę do funkcji? Na przykład chcę użyć nazwy hollerdla printf. #define jest oczywiste ... w inny sposób?

Rozwiązania:

  1. #define holler printf
  2. void (*p)() = fn; //function pointer
  3. void (&r)() = fn; //function reference
  4. inline void g(){ f(); }
Agnel Kurian
źródło
Dzięki Wam wszystkim. Moi koledzy będą zachwyceni void (&NewName)(some_vector&, float, float, float, float) = OldName;moim następnym zameldowaniem.
Agnel Kurian
19
nie tak bardzo, jak z przyjemnością zobaczą, jak używasz losowych nazw dla standardowych funkcji bibliotecznych.
jalf
2
Nie będę się tu bawić printf. To był tylko przykład. Problem tutaj bardziej dotyczy ograniczeń języka angielskiego niż czegokolwiek innego. Mam jedną funkcję służącą do celu A i celu B, ale po prostu nie mogę znaleźć tutaj jednej nazwy służącej do obu celów.
Agnel Kurian
2
@Neil, dokładnie. T &a = b;tworzy nową nazwę dla b. typedefdla typów i namespace A=B;dla przestrzeni nazw.
Agnel Kurian
2
Jest using BaseClass::BaseClassMethodi jest using AliasType = Type;i jest nawet namespace AliasNamespace = Namespace;. To, czego nam brakuje, tousing AliasFunction = Function;
anton_rh

Odpowiedzi:

114

Istnieją różne podejścia:

  • W C ++ 11 z funkcjami nie będącymi szablonami, które nie są przeciążone, możesz po prostu użyć:

    const auto& new_fn_name = old_fn_name;
  • Jeśli ta funkcja ma wiele przeciążeń, powinieneś użyć static_cast:

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

    Przykład: istnieją dwa przeciążenia funkcji std::stoi

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

    Jeśli chcesz utworzyć alias do pierwszej wersji, powinieneś użyć:

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

    Uwaga: nie ma sposobu na utworzenie aliasu do przeciążonej funkcji, tak aby wszystkie jej przeciążone wersje działały, dlatego zawsze należy określić, które dokładnie przeciążenie funkcji ma być wymagane.

  • Dzięki C ++ 14 możesz pójść jeszcze dalej ze constexprzmiennymi szablonowymi. To pozwala na tworzenie aliasów funkcji opartych na szablonach:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
  • Co więcej, zaczynając od C ++ 11 masz funkcję o nazwie, std::mem_fnktóra umożliwia aliasowanie funkcji składowych. Zobacz poniższy przykład:

    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
źródło
1
Doskonale, a co powiesz na C ++ 98? Mam klasę z 2 przeciążeniami „reset” używanymi do ustawiania i resetowania. Wewnętrznie nie ma problemu. Dla użytkowników zewnętrznych chciałem alias ustawić jako „ustaw”, więc jest to intuicyjne dla kontekstu (ustaw domyślną konstrukcję, wyczyść () 'd itp .; zresetuj działający obiekt). Metody klasowe: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "reset" z odpowiednimi podpisami działa. Ponieważ mam podpis w deklaracji klasy, czy mogę po prostu zainicjować klasę,: set (reset), set (reset). Jeśli nie, czy Twój wyraźny przykład static_cast zadziała?
Luv2code
8
Wydaje się, że występuje problem z constexprpodejściem do zmiennych szablonu: alias nie może wykonywać dedukcji typu. Kompilator wymaga ode mnie podania listy parametrów szablonu (piszę zmienną funkcję szablonu): nie może odwoływać się do szablonu zmiennej `alias_to_old bez listy argumentów szablonu
user69818
1
constexpr auto new_fn_name = old_fn_namedziała w C ++ 11 (przynajmniej w gcc 4.9.2) i jest lepsze niż umieszczanie &. Nie wymaga wywołania zawsze wykonywanego przez wskaźnik, dzięki czemu funkcja może być wstawiana w miejscu wywołania.
ony
Dzięki generycznym lambdom C ++ 14 udało mi się wykonać następujące czynności, które powinny również działać, gdy funkcja docelowa ma wiele przeciążeń: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall
1
Stosując std::mem_fnto nie aliasem ponieważ wykonuje znacznie więcej magii za sensie.
cgsdfc
35

Możesz utworzyć wskaźnik funkcji lub odwołanie do funkcji:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference
Brian R. Bondy
źródło
2
To zajmuje ciasto. Nie wiedziałem o odwołaniach do funkcji.
Agnel Kurian
@Vulcan: Są prawie takie same, ponieważ możesz wywołać ich obu z tą samą składnią, ale ich adresy są trochę inne. r nie zajmuje własnego miejsca w pamięci, przechowując adres.
Brian R. Bondy
1
Jak byś zadzwonił fn, używając aliasu? Czy możesz wyjaśnić wskaźnik funkcji i odniesienie do funkcji? Czym się różnią? Czy są tutaj takie same?
ma11hew 28
1
@Matt, nazywasz to dokładnie tak, jak nazywasz fn. r();
Agnel Kurian
jak byś to zrobił dla metody instancji? EDYCJA: wydaje się, że kompiluje się:void (&r)() = this->fn;
Sam
21
typedef int (*printf_alias)(const char*, ...);
printf_alias holler = std::printf;

Powinieneś dobrze.

jer
źródło
Czy printf nie znajduje się w globalnej przestrzeni nazw?
Agnel Kurian,
3
jest globalny, jeśli uwzględnisz <stdio.h>, ale w std, jeśli uwzględnisz <cstdio>
Injektilo
@einpoklum: Nie ma nic złego w decltype , ale odpowiedź pochodzi z 2010 roku. Wtedy nie było tak, decltypejak zostało wprowadzone w c ++ 11. Co więcej, powinno to również działać z dobrym, starym zwykłym C.
Phidelux
10

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

MSalters
źródło
7

Użyj wbudowanego opakowania. Otrzymujesz oba interfejsy API, ale zachowaj jedną implementację.

Jan
źródło
3

Od 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)...); \
}
żaglica009
źródło
1

Dzięki generycznym lambdom C ++ 14 mogłem wykonać następujące czynności, które powinny również działać, gdy funkcja docelowa ma wiele przeciążeń:

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };
Anthony Hall
źródło
Hah! To mnie zasmuca, nawet @ user5534993, który pierwotnie naciskał na przesłanie tej odpowiedzi, a potem nie mógł oddać głosu za twoją drogą. Cóż, masz jedną przy sobie.
FeRD
0

Warto tutaj wspomnieć IMO, że chociaż oryginalne pytanie (i świetne odpowiedzi) są zdecydowanie przydatne, jeśli chcesz zmienić nazwę funkcji (istnieją dobre powody, aby to zrobić!), Jeśli chcesz tylko usunąć głęboką przestrzeń nazw, ale zachowaj nazwę, jest usingdo tego słowo kluczowe:

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
}

Ma to również tę zaletę, że ogranicza się do zakresu, chociaż zawsze można go użyć na najwyższym poziomie pliku. Często używam tego dla couti endltak nie muszę sprowadzić wszystkich stdz klasycznym using namespace std;na początku pliku, ale także przydatne, jeśli używasz coś jak std::this_thread::sleep_for()wiele w jednym pliku lub funkcji, ale nie wszędzie, nie żadne inne funkcje z przestrzeni nazw. Jak zawsze, odradza się używanie go w plikach .h, w przeciwnym razie zanieczyszczasz globalną przestrzeń nazw.

To nie to samo, co „zmiana nazwy” powyżej, ale często jest to coś, czego naprawdę chcemy.

Kevin Anderson
źródło