โอเวอร์โหลดฟังก์ชันโดยใช้เทมเพลต


34

ฉันพยายามกำหนดฟังก์ชั่นโดยใช้เทมเพลตและฉันต้องการให้ typename เป็น int หรือ anEnum (enum เฉพาะที่ฉันกำหนดไว้) ฉันได้ลองทำสิ่งต่อไปนี้แล้ว แต่ล้มเหลว:

template <int | anEnum T> // or <int T, anEnum T> or <int, anEnum T>
bool isFunction(const T &aVariable){}

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

isFunction(aVariable) // and not isFunction<int> (aVariable) nor isFunction<anEnum> (aVariable)

โดยทั่วไปฉันต้องการให้ฟังก์ชั่นนี้เป็นเทมเพลตสำหรับประเภท int และ aNum ฉันค้นหาสิ่งนี้แล้ว แต่ไม่พบคำตอบ ฉันจะพลาดอะไรได้บ้าง ขอบคุณ,


หากเป็น enum เดียวหรือ int ประเภททำไมไม่เพียงแค่เขียนฟังก์ชั่นทั้งสอง? ทำไมคุณต้องมีแม่แบบในกรณีนั้น
Klaus

แล้วประเภทอื่น ๆ ล่ะ? คุณต้องการส่งคืนfalseสำหรับประเภทอื่น ๆ หรือไม่ต้องการให้อินสแตนซ์ของฟังก์ชันประเภทอื่นยกตัวอย่างเช่น
frogatto

@frogatto ไม่ค่าส่งคืน bool ไม่มีสิ่งใดที่มีประเภท
bg

@ Klaus ฉันขอให้เรียนรู้ทางเลือกอื่น จากคำตอบปัจจุบันฉันตัดสินใจที่จะเพียงกำหนดฟังก์ชั่นทั้งสองอย่าง
bg

คำตอบ:


25

นอกจากคำตอบที่ไม่ใช่ C ++ 20 หากคุณมีโอกาสสามารถใช้C ++ 20และconceptsคุณลักษณะของมันได้ฉันขอแนะนำให้คุณใช้งานต่อไปนี้:

#include <iostream>
#include <concepts>

enum class MyEnum {
    A,
    B,
    C
};

template <typename T>
concept IntegralOrEnum = std::same_as<MyEnum, T> || std::integral<T>;

template <IntegralOrEnum T>
bool isFunction(T const& aVariable) {
    return true;
}

int main() {
    isFunction(MyEnum::A);
    isFunction(3);
    isFunction("my_string"); // error
    return 0;
}

การสาธิต

UPDATE

ตามความเห็นของ@RichardSmithนี่เป็นวิธีที่ปรับขนาดได้และนำกลับมาใช้ใหม่ได้มากขึ้น:

template <typename T, typename ...U>
concept one_of = (std::is_same_v<T, U> || ...);

template <one_of<int, MyEnum> T>
bool isFunction(T const& aVariable) {
    return true;
}

สำหรับกรณีเฉพาะที่ต้องการให้ประเภทเป็นหนึ่งในสองประเภทเฉพาะบางอย่างเช่นนี้อาจทำงานได้ดีขึ้น:template<typename T, typename ...U> concept one_of = (std::is_same_v<T, U> || ...); template<one_of<int, MyEnum> T> bool isFunction(T const& aVariable) {
Richard Smith

1
@RichardSmith ฉันได้อัปเดตคำตอบของฉันด้วยแล้ว ฉันพบว่าสามารถใช้ซ้ำได้และปรับขนาดได้มากกว่านี้ ขอบคุณ
NutCracker

21

มีสองวิธีในการบรรลุเป้าหมายนี้ ทั้งหมดเกี่ยวข้องกับการใช้type_traitsส่วนหัว คุณสามารถยืนยันแบบคงที่กับประเภทที่เป็นปัญหาในเนื้อหาของฟังก์ชันตัวอย่างเช่น

หรือหากคุณต้องการพิจารณาฟังก์ชั่นนี้ในการโอเวอร์โหลดอื่น ๆ สามารถใช้เทคนิค SFINAE

template<typename T>
auto isFunction(const T &aVariable) 
  -> std::enable_if_t<std::is_same<T, int>::value || std::is_same<T,anEnum>::value, bool> {
}

สิ่งนี้จะลบฟังก์ชั่นออกจากชุดโอเวอร์โหลดก่อนที่มันจะถูกเรียกถ้าประเภทไม่ตรงกัน แต่ถ้าคุณไม่ต้องการพฤติกรรมนี้การยืนยันแบบคงที่จะช่วยให้เกิดข้อผิดพลาดที่เป็นมิตรกับโปรแกรมเมอร์มากขึ้น


3

แล้วโซลูชันนี้ล่ะ? รหัสที่มีฟังก์ชั่นจะถูกรวบรวมหากประเภท T เป็นไปตามข้อกำหนดของคุณ มิฉะนั้นการยืนยันแบบคงที่ล้มเหลว

#include <type_traits>
enum anEnum {
    //
};

template <typename T, bool defined = std::is_same<T, int>::value ||
                                     std::is_same<T, anEnum>::value>
bool isFunction(const T& aVariable)
{
    static_assert(defined, "Invalid specialization");

    bool result = false;
    // Put your code here
    return result;
}

1
วิธีนี้ใช้ไม่ได้กับการแก้ปัญหาการโอเวอร์โหลดหากมีลายเซ็นอื่นปรากฏอยู่ (เช่นสมมุติisFunction(std::string_view)) ลายเซ็นจะยังคงเป็นการจับคู่ที่ถูกต้อง แต่การเริ่มต้นทำให้เกิดข้อผิดพลาด
LF

คุณสามารถประกาศลายเซ็นที่ไร้ประโยชน์ตามที่ถูกลบได้: bool isFunction (std :: string_view) = delete;
ixjxk

ฉันกำลังพูดถึงเกินพิกัดเพิ่มเติม ในกรณีดังกล่าวลายเซ็นที่ไม่ถูกต้องนี้อาจจบลงด้วยการจับคู่ที่ตรงกัน (เช่นสำหรับตัวอักษรสตริง) ดังนั้นการบล็อกการโอเวอร์โหลด
LF

0

ฉันได้ปรับปรุงhttps://stackoverflow.com/a/60271100/12894563คำตอบ 'ถ้า constexpr' สามารถช่วยได้ในสถานการณ์นี้:

template <typename T>
struct always_false : std::false_type {};

template <typename T>
bool isFunction(const T& aVariable)
{
    if constexpr(std::is_same_v<T, int> || std::is_same_v<T, anEnum>)
    {
        std::cout << "int\n";
        // put your code here
        return true;
    }
    else
    {
        static_assert(always_false<T>::value, "You should declare non-template function or write if constexpr branch for your type");
        return false;
    }
}

bool isFunction(std::string_view)
{
    std::cout << "std::string_view\n";
    return true;
}

int main()
{
    isFunction(std::string_view("1L"));
    isFunction(1);
    //isFunction(1L); // will produce an error message from static_assert
}

isFunction (1L) จะล้มเหลวเนื่องจากไม่มีฟังก์ชั่นโอเวอร์โหลดหรือสาขา 'ถ้า constexpr'

อัปเดต: แก้ไขพลาด

template <typename T>
struct always_false : std::false_type {};

https://godbolt.org/z/eh4pVn


static_assert(false, ...)เป็น NDR ที่มีรูปแบบไม่ดีโดยไม่ได้ใช้งาน ถ้าคุณโชคดีผู้แปลจะบอกคุณทันทีเหมือนเสียงดังกราว godbolt.org/z/m_Gk9n
StoryTeller - Unslander Monica

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