Bagaimana cara menetapkan alias ke nama fungsi di C ++?

100

Sangat mudah untuk membuat nama baru untuk sebuah tipe, variabel, atau namespace. Tapi bagaimana cara menetapkan nama baru ke suatu fungsi? Misalnya, saya ingin menggunakan nama holleruntuk printf. #define sudah jelas ... ada cara lain?

Solusi:

  1. #define holler printf
  2. void (*p)() = fn; //function pointer
  3. void (&r)() = fn; //function reference
  4. inline void g(){ f(); }
Agnel Kurian
sumber
Terima kasih semuanya. Rekan-rekan saya akan senang melihatnya void (&NewName)(some_vector&, float, float, float, float) = OldName;di check-in berikutnya.
Agnel Kurian
19
tidak sebanyak mereka akan senang melihat Anda menggunakan nama acak untuk fungsi perpustakaan standar.
jalf
2
Saya tidak main-main dengan di printfsini. Itu hanya sebuah contoh. Masalahnya di sini lebih berkaitan dengan keterbatasan bahasa Inggris daripada hal lainnya. Saya memiliki satu fungsi yang melayani tujuan A dan tujuan B tetapi saya tidak dapat menemukan satu nama pun yang melayani kedua tujuan di sini.
Agnel Kurian
2
@Neil, tepatnya. T &a = b;membuat nama baru untuk b. typedefuntuk tipe dan namespace A=B;namespace.
Agnel Kurian
2
Ada using BaseClass::BaseClassMethod, dan ada using AliasType = Type;, dan bahkan ada namespace AliasNamespace = Namespace;. Apa yang kami lewatkan adalahusing AliasFunction = Function;
anton_rh

Jawaban:

114

Ada pendekatan yang berbeda:

  • Dengan C ++ 11 dengan fungsi non-template non-overloaded, Anda cukup menggunakan:

    const auto& new_fn_name = old_fn_name;
  • Jika fungsi ini memiliki banyak kelebihan, Anda harus menggunakan static_cast:

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

    Contoh: ada dua kelebihan fungsi std::stoi

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

    Jika Anda ingin membuat alias ke versi pertama Anda harus menggunakan yang berikut ini:

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

    Catatan: tidak ada cara untuk membuat alias ke fungsi yang kelebihan beban sedemikian rupa sehingga semua versinya yang kelebihan beban berfungsi, jadi Anda harus selalu menentukan fungsi yang kelebihan beban yang Anda inginkan.

  • Dengan C ++ 14 Anda dapat melangkah lebih jauh dengan constexprvariabel template. Itu memungkinkan Anda untuk membuat alias fungsi kerangka:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
  • Selain itu, dimulai dengan C ++ 11 Anda memiliki fungsi yang dipanggil std::mem_fnyang memungkinkan fungsi anggota alias. Lihat contoh berikut:

    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
sumber
1
Luar biasa, bagaimana dengan C ++ 98? Saya memiliki kelebihan beban "reset" kelas w / 2 yang digunakan untuk menyetel & menyetel ulang. Secara internal, tidak masalah. Untuk pengguna eksternal saya ingin alias sebagai "set" sehingga intuitif untuk konteks (set default-built, clear () 'd etc .; reset objek kerja). Metode kelas: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "reset" dengan tanda tangan yang sesuai melakukan pekerjaan. Karena saya memiliki tanda tangan di deklarasi kelas, dapatkah saya menginisialisasi kelas,: set (reset), set (reset). Jika tidak, apakah contoh static_cast eksplisit Anda akan berfungsi?
Luv2code
8
Tampaknya ada masalah dengan constexprpendekatan variabel template: alias tidak dapat melakukan pengurangan tipe. Kompiler mengharuskan saya untuk memberikan daftar parameter template (Saya menulis fungsi template variadic): tidak dapat merujuk ke template variabel `alias_to_old 'tanpa daftar argumen template
user69818
1
constexpr auto new_fn_name = old_fn_namebekerja di C ++ 11 (setidaknya di gcc 4.9.2) dan lebih baik daripada menempatkan &. Itu tidak memerlukan panggilan untuk selalu dilakukan melalui pointer dan dengan demikian memungkinkan fungsi menjadi sebaris di tempat panggilan.
ony
Dengan lambda generik C ++ 14, saya dapat melakukan hal berikut, yang seharusnya juga berfungsi ketika fungsi target memiliki beberapa kelebihan beban: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall
1
Menggunakan std::mem_fnini bukan alias karena melakukan jauh lebih ajaib balik arti.
cgsdfc
35

Anda dapat membuat penunjuk fungsi atau referensi fungsi:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference
Brian R. Bondy
sumber
2
Ini mengambil kuenya. Saya tidak tahu tentang referensi fungsi.
Agnel Kurian
@Vulcan: Mereka hampir sama karena Anda dapat memanggil keduanya dengan sintaks yang sama, tetapi alamatnya sedikit berbeda. r tidak menggunakan ruang memorinya sendiri untuk menyimpan alamat.
Brian R. Bondy
1
Bagaimana Anda menelepon fn, menggunakan alias? Bisakah Anda menjelaskan penunjuk fungsi & referensi fungsi? Bagaimana mereka berbeda? Apakah mereka sama di sini?
ma11hew28
1
@Matt, Anda menyebutnya persis seperti yang Anda sebut fn. r();
Agnel Kurian
bagaimana Anda melakukan ini untuk metode contoh? EDIT: ini sepertinya mengkompilasi:void (&r)() = this->fn;
Sam
21
typedef int (*printf_alias)(const char*, ...);
printf_alias holler = std::printf;

Haruskah kamu baik-baik saja.

jer
sumber
Bukankah printf ada di namespace global?
Agnel Kurian
3
ini global jika Anda menyertakan <stdio.h>, tetapi dalam std jika Anda menyertakan <cstdio>
Injektilo
@einpoklum: Tidak ada yang salah dengan dectype , tapi jawabannya dari tahun 2010. Saat itu belum ada decltypeseperti yang diperkenalkan di c ++ 11. Selain itu, ini juga harus bekerja dengan C. dataran tua yang bagus
Phidelux
10

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

MSalters
sumber
7

Gunakan pembungkus sebaris. Anda mendapatkan kedua API tersebut, tetapi tetap mempertahankan penerapan tunggal.

John
sumber
3

Dari 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)...); \
}
sailfish009
sumber
1

Dengan lambda generik C ++ 14, saya dapat melakukan hal berikut, yang seharusnya juga berfungsi ketika fungsi target memiliki beberapa kelebihan beban:

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };
Anthony Hall
sumber
Hah! Itu membuat saya sedih, bahkan @ user5534993 yang awalnya mendorong Anda untuk mengirimkan jawaban ini, kemudian tidak bisa memberi suara positif. Nah, ini, miliki satu untukku.
FeRD
0

Perlu disebutkan di sini IMO bahwa sementara pertanyaan asli (dan jawaban yang bagus) pasti berguna jika Anda ingin mengganti nama suatu fungsi (ada alasan bagus untuk melakukannya!), Jika semua yang ingin Anda lakukan adalah menghapus namespace yang dalam tetapi simpan namanya, ada usingkata kunci untuk ini:

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
}

Ini juga memiliki keuntungan karena terbatas pada ruang lingkup, meskipun Anda selalu dapat menggunakannya di tingkat atas file. Saya sering menggunakan ini untuk coutdan endljadi saya tidak perlu stdmemasukkan SEMUA dengan yang klasik using namespace std;di bagian atas file, tetapi juga berguna jika Anda menggunakan sesuatu sepertistd::this_thread::sleep_for() banyak dalam satu file atau fungsi, tetapi tidak di semua tempat, dan bukan fungsi lain dari namespace. Seperti biasa, tidak disarankan untuk menggunakannya dalam file .h, atau Anda akan mencemari namespace global.

Ini tidak sama dengan "penggantian nama" di atas, tetapi sering kali yang benar-benar diinginkan.

Kevin Anderson
sumber