Làm cách nào để gán bí danh cho tên hàm trong C ++?


100

Thật dễ dàng để tạo tên mới cho một kiểu, một biến hoặc một không gian tên. Nhưng làm cách nào để gán tên mới cho một hàm? Ví dụ, tôi muốn sử dụng tên hollercho printf. #define là hiển nhiên ... còn cách nào khác không?

Các giải pháp:

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

Cảm ơn tất cả các bạn. Các đồng nghiệp của tôi sẽ thích nó khi thấy void (&NewName)(some_vector&, float, float, float, float) = OldName;trong lần đăng ký tiếp theo của tôi.
Agnel Kurian

19
không nhiều vì họ sẽ thích thấy bạn sử dụng các tên ngẫu nhiên cho các hàm thư viện tiêu chuẩn.
jalf

2
Tôi không làm phiền printfở đây. Đó chỉ là một ví dụ. Vấn đề ở đây liên quan nhiều đến hạn chế của tiếng Anh hơn bất cứ điều gì khác. Tôi có một chức năng duy nhất phục vụ mục đích A và mục đích B nhưng tôi chỉ đơn giản là không thể tìm thấy một tên duy nhất phục vụ cả hai mục đích ở đây.
Agnel Kurian

2
@Neil, chính xác. T &a = b;tạo một tên mới cho b. typedefcho các loại và namespace A=B;cho không gian tên.
Agnel Kurian

2
using BaseClass::BaseClassMethod, và có using AliasType = Type;, và có namespace AliasNamespace = Namespace;. Những gì chúng tôi đang thiếu làusing AliasFunction = Function;
anton_rh

Câu trả lời:


114

Có các cách tiếp cận khác nhau:

  • Với C ++ 11 với các hàm không quá tải không phải mẫu, bạn có thể chỉ cần sử dụng:

    const auto& new_fn_name = old_fn_name;
  • Nếu hàm này có nhiều quá tải, bạn nên sử dụng static_cast:

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

    Ví dụ: có hai hàm quá tải std::stoi

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

    Nếu bạn muốn tạo bí danh cho phiên bản đầu tiên, bạn nên sử dụng như sau:

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

    Lưu ý: không có cách nào tạo bí danh cho hàm quá tải để tất cả các phiên bản quá tải của nó hoạt động, vì vậy bạn phải luôn chỉ định chính xác hàm quá tải nào bạn muốn.

  • Với C ++ 14, bạn có thể tiến xa hơn nữa với constexprcác biến mẫu. Điều đó cho phép bạn đặt bí danh cho các hàm được tạo mẫu:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
  • Hơn nữa, bắt đầu với C ++ 11, bạn có một hàm được gọi std::mem_fncho phép đặt bí danh là các hàm thành viên. Xem ví dụ sau:

    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
Tuyệt vời, còn C ++ 98 thì sao? Tôi có một lớp w / 2 quá tải "đặt lại" được sử dụng để thiết lập và đặt lại. Trong nội bộ, không có vấn đề. Đối với người dùng bên ngoài, tôi muốn đặt bí danh là "set" để nó trực quan cho ngữ cảnh (đặt một cấu trúc mặc định, clear () 'd vv; đặt lại đối tượng làm việc). Phương thức lớp: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "đặt lại" w / chữ ký tương ứng thực hiện công việc. Vì tôi có chữ ký trong khai báo lớp, tôi có thể chỉ khởi tạo lớp,: set (đặt lại), set (đặt lại). Nếu không, ví dụ static_cast rõ ràng của bạn có hoạt động không?
Luv2code

8
Dường như có vấn đề với constexprphương pháp tiếp cận biến mẫu: bí danh không thể thực hiện suy luận kiểu. Trình biên dịch đòi hỏi tôi phải cung cấp danh sách mẫu tham số (Tôi viết một mẫu chức năng variadic): không có thể tham khảo mẫu biến 'alias_to_old' mà không có một danh sách mẫu tranh luận
user69818

1
constexpr auto new_fn_name = old_fn_namehoạt động trong C ++ 11 (ít nhất là trong gcc 4.9.2) và tốt hơn là đặt &. Nó không yêu cầu cuộc gọi luôn được thực hiện thông qua con trỏ và do đó cho phép hàm được nội tuyến thay cho lời gọi.
ony

Với ++ 14 lambdas generic C, tôi đã có thể làm những điều sau đây, mà cũng nên làm việc khi hàm mục tiêu có nhiều quá tải: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall

1
Sử dụng std::mem_fnkhông một bí danh vì nó thực hiện nhiều hơn nữa kỳ diệu đằng sau ý nghĩa.
cgsdfc 21/02/19

35

Bạn có thể tạo một con trỏ hàm hoặc một tham chiếu hàm:

void fn()
{
}

//...

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

2
Điều này làm mất bánh. Tôi không biết về tham chiếu chức năng.
Agnel Kurian

@Vulcan: Chúng gần như giống nhau ở chỗ bạn có thể gọi cả hai với cùng một cú pháp, nhưng địa chỉ của chúng có một chút khác biệt. r không chiếm không gian bộ nhớ riêng của nó để giữ một địa chỉ.
Brian R. Bondy

1
Bạn sẽ gọi như thế nào fn, sử dụng bí danh? Bạn có thể giải thích con trỏ hàm & tham chiếu hàm không? Họ khác nhau như thế nào? Ở đây chúng có giống nhau không?
ma11hew28

1
@Matt, Bạn gọi nó chính xác như bạn sẽ gọi fn. r();
Agnel Kurian

bạn sẽ làm điều này như thế nào đối với một phương thức instance? CHỈNH SỬA: điều này dường như biên dịch:void (&r)() = this->fn;
Sam

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

Bạn nên làm tốt.


Không phải printf trong không gian tên chung?
Agnel Kurian

3
nó toàn cầu nếu bạn bao gồm <stdio.h>, nhưng trong std nếu bạn bao gồm <cstdio>
Injektilo

@einpoklum: Không có gì sai với kiểu khai báo , nhưng câu trả lời là từ năm 2010. Hồi đó không có decltypenhư nó được giới thiệu trong c ++ 11. Hơn nữa này cũng nên làm việc với tốt C. đồng bằng cũ
Phidelux


7

Sử dụng một trình bao bọc nội tuyến. Bạn nhận được cả hai API, nhưng vẫn duy trì triển khai đơn lẻ.


3

Từ thông thạocpp : 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)...); \
}

0

Điều đáng nói ở đây IMO là mặc dù câu hỏi ban đầu (và những câu trả lời tuyệt vời) chắc chắn hữu ích nếu bạn muốn đổi tên một hàm (có những lý do chính đáng để làm như vậy!), Nếu tất cả những gì bạn muốn làm là loại bỏ một không gian tên sâu nhưng giữ tên, có usingtừ khóa cho điều này:

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
}

Điều này cũng có lợi thế là nó được giới hạn trong một phạm vi, mặc dù bạn luôn có thể sử dụng nó ở cấp cao nhất của tệp. Tôi thường sử dụng cái này cho coutendlvì vậy tôi không cần đưa TẤT CẢ những stdcái cổ điển using namespace std;ở đầu tệp, nhưng cũng hữu ích nếu bạn đang sử dụng cái gì đó nhưstd::this_thread::sleep_for() nhiều thứ trong một tệp hoặc hàm, nhưng không phải ở mọi nơi, và không phải bất kỳ chức năng nào khác từ không gian tên. Như mọi khi, không khuyến khích sử dụng nó trong các tệp .h, nếu không bạn sẽ làm ô nhiễm không gian tên chung.

Điều này không giống như "đổi tên" ở trên, nhưng thường là những gì thực sự muốn.


0

Với lambdas chung C ++ 14, tôi đã có thể làm như sau, điều này cũng sẽ hoạt động khi hàm đích có nhiều lần quá tải:

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.