ฉันจะป้องกันไม่ให้ C ++ คาดเดาอาร์กิวเมนต์เท็มเพลตที่สองได้อย่างไร


26

ฉันใช้ไลบรารี่ C ++ ( strf ) ซึ่งบางที่อยู่ภายในนั้นมีรหัสต่อไปนี้:

namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }

template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}

ตอนนี้ฉันต้องการใช้strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)ในรหัสของฉัน แต่ถ้าฉันทำฉันได้รับข้อผิดพลาดต่อไปนี้ (ด้วย NVCC ของ CUDA 10.1):

error: more than one instance of overloaded function "strf::range" matches the argument list:
            function template "auto strf::range(ForwardIt, ForwardIt)"
            function template "auto strf::range(const Range &, const CharT *)"
            argument types are: (util::constexpr_string::const_iterator, util::constexpr_string::const_iterator)

ห้องสมุดรหัสอาจจะสามารถเปลี่ยนแปลงได้เพื่อหลีกเลี่ยงปัญหานี้ (เช่นใช้:

inline auto range(const typename std::enable_if<not std::is_pointer<typename std::remove_cv<Range>::type>::value, Range &>::type range, const CharT* sep)

เพื่อให้แน่ใจว่าRangeไม่ใช่ตัวชี้); แต่ตอนนี้ฉันไม่สามารถเปลี่ยนแปลงได้ แต่ฉันต้องการบ่งบอกคอมไพเลอร์ที่ฉันหมายถึงมีอาร์กิวเมนต์เท็มเพลตเพียงตัวเดียวเท่านั้นไม่ได้ระบุและอีกอันหนึ่งอนุมานไว้

ฉันจะทำสิ่งนั้นได้ไหม

จะขอบคุณคำตอบสำหรับ C ++ 11 และ C ++ 14; C ++ 17 คำตอบที่เกี่ยวข้องกับคำแนะนำในการหักเงินมีความเกี่ยวข้องน้อยกว่า แต่ถ้าคุณมีคำตอบกรุณาโพสต์มัน (สำหรับรุ่น NVCC ในอนาคต ...


อัปเดต:ไลบรารี strf ได้รับการอัปเดตเพื่อหลีกเลี่ยงสถานการณ์นี้ แต่คำถามนี้ได้รับการตั้งคำถาม


1
ฉันคาดเดาว่าจะผ่านตัววนซ้ำแบบกำหนดเองซึ่งบางตัวจะพันกันchar*แต่ไม่มีใครแก้ปัญหาใช่ไหม
Konrad Rudolph

1
@ KonradRudolph: นั่นเป็นวิธีแก้ปัญหา แต่ไม่ตอบคำถามของฉัน ฉันมีวิธีแก้ไขปัญหาอื่นอยู่แล้ว (เฉพาะสิ่งที่อยู่ใน /*...*/) แต่ฉันต้องการใช้ถนนสูงที่นี่
einpoklum

1
ในกรณีนั้นคำตอบของฉัน (เดา) คือ "ไม่สามารถทำได้" โชคไม่ดี เพื่อความเป็นธรรมฉันไม่แน่ใจว่าฉันจะยอมรับวิธีแก้ปัญหาที่แนะนำในรหัสของฉันเอง
Konrad Rudolph

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

@ walnut: วิธีแก้ปัญหาทั่วไปจะดีกว่า; สถานการณ์เฉพาะของฉันส่วนใหญ่เป็นแรงจูงใจสำหรับปัญหา
einpoklum

คำตอบ:


16
template<typename T>
inline constexpr auto range1_ptr = strf::range<T>;

template<typename T>
inline decltype(auto) range1(T begin, T end) {
    return range1_ptr<T>(begin, end);
}

แล้วโทรแทนrange1strf::range

range1_ptr<T>(...)สามารถใช้เพื่อเรียกเทมเพลตโดยใช้อาร์กิวเมนต์เท็มเพลตเดียวได้อย่างชัดเจน แต่ไม่ได้ทำการหักจากอาร์กิวเมนต์ range1ทำซ้ำการหักจากstrf::rangeเทมเพลตดั้งเดิม

วิธีนี้ใช้งานได้เนื่องจาก[temp.deduct.funcaddr] / 1กล่าวว่าการหักล้างอาร์กิวเมนต์เทมเพลตเมื่อรับที่อยู่ของฟังก์ชันโดยไม่มีประเภทเป้าหมายของการแปลงจะทำในเทมเพลตฟังก์ชั่นผู้สมัครแต่ละคนราวกับว่าพารามิเตอร์และรายการอาร์กิวเมนต์ของการโทรตามสมมุติฐานนั้น ว่างเปล่า ดังนั้นจึงไม่สามารถอนุมานเท็มเพลตอาร์กิวเมนต์ที่สองสำหรับการโอเวอร์โหลดที่สองพร้อมกับพารามิเตอร์เท็มเพลตสองตัว ทางเลือกเดียวที่เหลือคือการโอเวอร์โหลดครั้งแรกซึ่งจะถูกเลือกให้เป็นเป้าหมายของตัวชี้ฟังก์ชั่น

ตราบใดที่ไม่มีเท็มเพลตฟังก์ชั่นตัวเลือกที่สองซึ่งเท็มเพลต id ที่ถูกต้องที่มีเพียงอาร์กิวเมนต์เดียวเท่านั้นที่สามารถสร้างขึ้นได้range1_ptrสามารถใช้เพื่อเรียกเทมเพลตฟังก์ชั่นที่รับอาร์กิวเมนต์เดียวได้อย่างชัดเจน มิฉะนั้นการสร้างอินสแตนซ์ของrange1_ptrจะทำให้เกิดข้อผิดพลาดเนื่องจากความกำกวม


จะมีความกำกวมstrf::range<T>หรือไม่
einpoklum

1
@ einpoklum มันคอมไพล์ดีกับ GCC และ Clang ฉันไม่ได้ตรวจสอบมาตรฐาน แต่จะแปลกใจถ้ามันควรจะคลุมเครือ
วอลนัต

บางทีคุณควรเปลี่ยนชื่อฟังก์ชั่นเป็นpretty_please_with_sugar_on_top()? ... C ++ บางครั้งก็แปลกมาก ...
einpoklum

คุณจัดการเพื่อ
คลาย

11

สิ่งที่เกี่ยวกับผ่านusing?

using tfp = void(*)(char const *, char const *);

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);

และคอมไพล์นี้? บรรทัดที่สองดูน่าสงสัยเป็นพิเศษ
einpoklum

@einpoklum - ตลกใช่มั้ย
สูงสุด 66

@einpoklum - น่าเสียดายที่ไม่ใช่คำตอบทั่วไป ทำงานได้ในกรณีนี้เพราะ (ถ้าฉันไม่ผิด) เฉพาะrange()รุ่นแรกที่เข้ากันได้กับtpf; กรณีอื่นอาจแตกต่างกัน
สูงสุด 66

@einpoklum - ในบรรทัดที่สองคุณสามารถอธิบายพารามิเตอร์เทมเพลต ( tfp x = &strf::range<char const *>;); ด้วยวิธีนี้ฉันคิดว่าคุณมีทางออกทั่วไปเกือบเท่ากับวอลนัท
max66

0

ทางออกคือ

1) ก่อนอื่นคุณควรระบุประเภทของอาร์กิวเมนต์ที่สองเช่น (char *)(some_char_ptr + some_length)

2) อย่าใช้constสำหรับทั้งคู่สิ่งนี้ใช้ได้ดี:

strf::range((char *)some_char_ptr, (char *)(some_char_ptr + some_length));

คุณสามารถลองแทนที่(char *)ด้วย(const char *)ซ้ายหรือขวามันยังคงใช้งานได้


นี่เป็นเรื่องที่น่าเกลียดถ้าข้อโต้แย้งชี้ไปที่constข้อมูล
aschepler

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