ฉันจะกำหนดนามแฝงให้กับชื่อฟังก์ชันใน C ++ ได้อย่างไร


101

การสร้างชื่อใหม่สำหรับประเภทตัวแปรหรือเนมสเปซนั้นง่ายมาก แต่ฉันจะตั้งชื่อใหม่ให้กับฟังก์ชันได้อย่างไร? ตัวอย่างเช่นฉันต้องการใช้ชื่อhollerสำหรับprintf. #define ชัดเจน ... มีวิธีอื่นไหม

แนวทางแก้ไข:

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

ขอบคุณทุกท่าน. เพื่อนร่วมงานของฉันจะชอบที่จะเห็นvoid (&NewName)(some_vector&, float, float, float, float) = OldName;ในการเช็คอินครั้งต่อไปของฉัน
Agnel Kurian

19
ไม่มากเท่ากับที่พวกเขาชอบเห็นคุณใช้ชื่อสุ่มสำหรับฟังก์ชันไลบรารีมาตรฐาน
jalf

2
ฉันจะไม่ยุ่งกับprintfที่นี่ นั่นเป็นเพียงตัวอย่างเท่านั้น ปัญหานี้เกี่ยวข้องกับข้อ จำกัด ของภาษาอังกฤษมากกว่าสิ่งอื่นใด ฉันมีฟังก์ชันเดียวที่ตอบสนองวัตถุประสงค์ A และวัตถุประสงค์ B แต่ฉันไม่พบชื่อเดียวที่ตอบสนองวัตถุประสงค์ทั้งสองที่นี่
Agnel Kurian

2
@ นีลแม่น. สร้างชื่อใหม่สำหรับT &a = b; สำหรับประเภทและสำหรับเนมสเปซ btypedefnamespace A=B;
Agnel Kurian

2
มีusing BaseClass::BaseClassMethodและมีและมีแม้กระทั่งusing AliasType = Type; namespace AliasNamespace = Namespace;สิ่งที่เราขาดหายไปคือusing AliasFunction = Function;
anton_rh

คำตอบ:


114

มีแนวทางที่แตกต่างกัน:

  • ด้วย C ++ 11 พร้อมฟังก์ชั่นที่ไม่โอเวอร์โหลดที่ไม่ใช่เทมเพลตคุณสามารถใช้:

    const auto& new_fn_name = old_fn_name;
    
  • หากฟังก์ชันนี้มีการโอเวอร์โหลดหลายครั้งคุณควรใช้static_cast:

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

    ตัวอย่าง: มีฟังก์ชันโอเวอร์โหลดสองฟังก์ชัน std::stoi

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

    หากคุณต้องการสร้างนามแฝงเป็นเวอร์ชันแรกคุณควรใช้สิ่งต่อไปนี้:

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

    หมายเหตุ:ไม่มีวิธีใดที่จะสร้างนามแฝงให้กับฟังก์ชันที่โอเวอร์โหลดเพื่อให้เวอร์ชันที่โอเวอร์โหลดทั้งหมดทำงานได้ดังนั้นคุณควรระบุเสมอว่าฟังก์ชันใดที่โอเวอร์โหลดที่คุณต้องการ

  • ด้วย C ++ 14 คุณสามารถไปได้ไกลกว่าเดิมด้วยconstexprตัวแปรเทมเพลต ที่ช่วยให้คุณใช้แทนฟังก์ชัน templated:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
    
  • ยิ่งไปกว่านั้นเริ่มต้นด้วย C ++ 11 คุณมีฟังก์ชันที่เรียกstd::mem_fnว่าอนุญาตให้ใช้แทนฟังก์ชันสมาชิก ดูตัวอย่างต่อไปนี้:

    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
ยอดเยี่ยมแล้ว C ++ 98 ล่ะ? ฉันมีคลาสที่มีการ "รีเซ็ต" มากเกินไปที่ใช้ในการตั้งค่าและรีเซ็ต ภายในไม่มีปัญหา สำหรับผู้ใช้ภายนอกฉันต้องการใช้นามแฝงว่า "set" ดังนั้นจึงใช้งานง่ายสำหรับบริบท (ตั้งค่า default-built, clear () 'd เป็นต้น; รีเซ็ตวัตถุการทำงาน) วิธีการคลาส: (1) "void (& set) (const string &, const bool, const bool);" (2) โมฆะ (& set) (สตริง const &, const int, const bool); 2 "รีเซ็ต" พร้อมลายเซ็นที่เกี่ยวข้องจะทำงาน เนื่องจากฉันมีลายเซ็นในการประกาศคลาสฉันสามารถเริ่มต้นคลาส: set (รีเซ็ต), ตั้งค่า (รีเซ็ต) ได้ไหม หากไม่เป็นเช่นนั้นตัวอย่าง static_cast ที่ชัดเจนของคุณจะใช้งานได้หรือไม่
Luv2code

8
ดูเหมือนว่าจะมีปัญหากับconstexprวิธีการของตัวแปรเทมเพลต: นามแฝงไม่สามารถทำการหักประเภทได้ คอมไพเลอร์ต้องการให้ฉันจัดเตรียมรายการพารามิเตอร์เทมเพลต (ฉันกำลังเขียนฟังก์ชันเทมเพลตตัวแปร): ไม่สามารถอ้างถึงเทมเพลตตัวแปรʻalias_to_old 'โดยไม่มีรายการอาร์กิวเมนต์เทมเพลต
user69818

1
constexpr auto new_fn_name = old_fn_nameผลงานใน C ++ 11 (อย่างน้อยใน GCC 4.9.2) &และดีกว่าการวาง ไม่จำเป็นต้องมีการโทรผ่านตัวชี้เสมอไปและทำให้ฟังก์ชันถูกอินไลน์แทนการโทร
ony

ด้วย C ++ 14 lambdas ทั่วไปฉันสามารถทำสิ่งต่อไปนี้ได้ซึ่งควรใช้งานได้เมื่อฟังก์ชันเป้าหมายมีการโอเวอร์โหลดหลายครั้ง: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall

1
ใช้std::mem_fnเป็นไม่นามแฝงเพราะมันดำเนินมายากลอื่น ๆ อีกมากมายที่อยู่เบื้องหลังความรู้สึก
cgsdfc

35

คุณสามารถสร้างตัวชี้ฟังก์ชันหรือการอ้างอิงฟังก์ชัน:

void fn()
{
}

//...

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

2
นี่ใช้เค้ก ฉันไม่รู้เกี่ยวกับการอ้างอิงฟังก์ชัน
Agnel Kurian

@ วัลแคน: เกือบจะเหมือนกันตรงที่คุณสามารถเรียกทั้งสองด้วยไวยากรณ์เดียวกัน แต่ที่อยู่ของพวกเขาแตกต่างกันเล็กน้อย r ไม่ใช้พื้นที่หน่วยความจำของตัวเองที่มีที่อยู่
Brian R.Bondy

1
คุณจะโทรfnโดยใช้นามแฝงว่าอย่างไร? คุณสามารถอธิบายตัวชี้ฟังก์ชันและการอ้างอิงฟังก์ชันได้หรือไม่? แตกต่างกันอย่างไร? พวกนี้เหมือนกันไหม
ma11hew28

1
@ คุณแมทเรียกตรง ๆ ว่าจะเรียกว่า fn r();
Agnel Kurian

คุณจะทำอย่างไรกับวิธีการอินสแตนซ์ แก้ไข: ดูเหมือนว่าจะรวบรวม:void (&r)() = this->fn;
แซม

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

คุณควรจะสบายดี


printf อยู่ในเนมสเปซส่วนกลางไม่ใช่หรือ
Agnel Kurian

3
เป็นสากลถ้าคุณรวม <stdio.h> แต่ในมาตรฐานถ้าคุณรวม <cstdio>
Injektilo

@einpoklum: ไม่มีอะไรผิดปกติกับประเภทเดสก์ท็อปแต่คำตอบคือตั้งแต่ปี 2010 ย้อนกลับไปตอนนั้นไม่decltypeได้มีการแนะนำใน c ++ 11 นอกจากนี้ควรใช้กับ C. ธรรมดาเก่า ๆ ที่ดี
Phidelux



3

จาก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

ด้วย C ++ 14 lambdas ทั่วไปฉันสามารถทำสิ่งต่อไปนี้ได้ซึ่งควรใช้งานได้เมื่อฟังก์ชันเป้าหมายมีการโอเวอร์โหลดหลายครั้ง:

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

ห๊ะ! นั่นทำให้ฉันเสียใจแม้แต่ @ user5534993 ที่ผลักดันให้คุณส่งคำตอบนี้ แต่แรกก็ไม่สามารถโหวตเพิ่มได้ ที่นี่มีหนึ่งกับฉัน
FeRD

0

ควรกล่าวถึงที่นี่ IMO ว่าในขณะที่คำถามดั้งเดิม (และคำตอบที่ยอดเยี่ยม) มีประโยชน์อย่างแน่นอนหากคุณต้องการเปลี่ยนชื่อฟังก์ชัน (มีเหตุผลที่ดีที่จะทำเช่นนั้น!) หากสิ่งที่คุณต้องการทำคือตัดเนมสเปซที่ลึกออกไป แต่ เก็บชื่อไว้มีusingคีย์เวิร์ดสำหรับสิ่งนี้:

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
}

นอกจากนี้ยังมีข้อดีของการ จำกัด ขอบเขตแม้ว่าคุณจะสามารถใช้งานได้ที่ระดับบนสุดของไฟล์ ฉันมักจะใช้สิ่งนี้เพื่อcoutและendlดังนั้นฉันจึงไม่จำเป็นต้องนำทั้งหมดมาstdด้วยคลาสสิกusing namespace std;ที่ด้านบนของไฟล์ แต่ก็มีประโยชน์เช่นกันหากคุณใช้สิ่งที่คล้ายstd::this_thread::sleep_for()กันมากในไฟล์หรือฟังก์ชันเดียว แต่ไม่ใช่ทุกที่และ ไม่ใช่ฟังก์ชันอื่นใดจากเนมสเปซ เช่นเคยเราไม่แนะนำให้ใช้ในไฟล์. h มิฉะนั้นคุณจะทำให้เนมสเปซทั่วโลกเป็นมลพิษ

นี่ไม่เหมือนกับการ "เปลี่ยนชื่อ" ด้านบน แต่มักเป็นสิ่งที่ต้องการจริงๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.