เทมเพลตโอเวอร์โหลดที่ไม่ชัดเจน


16

ฉันมีรหัสเทมเพลตต่อไปนี้

#include <vector>
#include <array>
#include <iostream>

template<typename T1>
void foo(std::vector<T1> bar) {
    std::cout << "GENERIC" << std::endl;
}

template<typename T1>
void foo(std::vector<std::vector<T1>> bar) {
    std::cout << "SPECIFIC (vector)" << std::endl;
}

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::vector<int>> a(2, std::vector<int> { 1, 2, 3});
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(a);
    foo(b);
}

ซึ่งผลิต

SPECIFIC (vector)
GENERIC

ฉันสงสัยว่าทำไมเวอร์ชันเวกเตอร์ของเวกเตอร์จึงถูกเรียกพร้อมกับเท็มเพลตเฉพาะ แต่เวอร์ชันเวกเตอร์ของอาร์เรย์นั้นมีชื่อเรียกทั่วไป


2
FYI: คุณสามารถทำให้สิ่งนี้ง่ายขึ้นด้วยปัญหาเดียวกันโดยลบส่วนนอกvectorทั้งหมด ดูที่นี่
ChrisMM

@ChrisMM จับได้ดี ตัวอย่างนี้ถูกสังเคราะห์จากรหัสการผลิตของฉันซึ่งจำเป็นต้องใช้โครงสร้างแบบซ้อน
Xaser

5
MSVC เรียกรุ่น vector-of-arrays: godbolt.org/z/7Gfeb0
R2RT

คำตอบ:


8
template<typename T1, size_t SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

คุณควรใช้แทน std::size_t ทำงานที่นี่int

แก้ไข: จริงๆแล้วความคิดเห็นและปรีชาญาณของฉันเกี่ยวกับรหัสทำให้ฉันขุดลงในหัวข้อ ได้อย่างรวดเร็วก่อนผู้พัฒนามาตรฐาน (เช่นฉัน) คาดว่าคอมไพเลอร์จะแปลงintเป็นstd::size_t(เพราะทั้งสองประเภทที่สมบูรณ์และการแปลงโดยปริยายเป็นเรื่องเล็กน้อยมาก) และเลือกvoid foo(std::vector<std::array<T1, SIZE>> bar)เป็นผู้เชี่ยวชาญที่ดีที่สุด ดังนั้นในขณะที่อ่านหน้าการหักอาร์กิวเมนต์ของแม่แบบฉันพบสิ่งนี้

หากใช้พารามิเตอร์เท็มเพลตที่ไม่ใช่ชนิดในรายการพารามิเตอร์และอาร์กิวเมนต์เท็มเพลตที่สอดคล้องกันนั้นถูกอนุมานประเภทของอาร์กิวเมนต์เท็มเพลตที่อนุมานได้ (ตามที่ระบุในรายการพารามิเตอร์เทมเพลตที่ปิดล้อมหมายความว่าการอ้างอิงจะถูกสงวนไว้) ต้องตรงกับประเภทของ พารามิเตอร์เท็มเพลตที่ไม่ใช่ประเภทแน่นอนยกเว้นว่า cv-qualifiers ถูกดร็อปและยกเว้นว่าอาร์กิวเมนต์เท็มเพลตถูกอนุมานจากอาเรย์ที่ถูกผูกไว้ - ในกรณีนั้นอนุญาตให้ใช้ประเภทอินทิกรัลชนิดใดก็ได้แม้แต่บูลแม้ว่ามันจะเป็นจริงเสมอ:

แน่นอนเช่นเคยคุณต้องอ่านสองสามครั้งมากกว่าหนึ่งครั้งเพื่อเข้าใจว่ามันแปลว่าอะไร :)

ดังนั้นผลลัพธ์ที่น่าสนใจออกมา

ความเชี่ยวชาญพิเศษที่เราต้องการไม่ได้ถูกเลือก แต่ถ้าคอมไพเลอร์ถูกบังคับให้เลือกมันจะเป็นข้อผิดพลาด

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(b); // P = std::vector<std::array<int,(int)SIZE>
            // A = std::vector<std::array<int,(unsigned_long)SIZE>>
            // error: deduced non-type template argument does not have the same
            // type as its corresponding template argument */
}

เรียกใช้รหัส

อีกสิ่งที่น่าสนใจคือ:

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

#include <vector>
#include <array>
#include <iostream>

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo<int,3>(b);
}

เรียกใช้รหัส


@ Xaser เนื่องจากอาร์กิวเมนต์เทมเพลตที่สองของอาร์เรย์เป็นประเภทsize_t...
Jean-Baptiste Yunès

2
เมื่อพิจารณาความคิดเห็นของ R2RT แล้วจะมีความแตกต่างเฉพาะของคอมไพเลอร์
Xaser

8

ฉันคิดว่านี่เป็นเพียงเพราะหนึ่งบรรทัด[temp.deduct.call]/4

โดยทั่วไปขั้นตอนการหักเงินจะพยายามค้นหาค่าอาร์กิวเมนต์เทมเพลตที่จะทำให้ A ที่อนุมานได้เหมือนกับ A

เพื่อชี้แจงAหมายถึงพารามิเตอร์จาก[temp.deduct.call]/1

... การหักอาร์กิวเมนต์แม่แบบพร้อมชนิดของอาร์กิวเมนต์ที่เกี่ยวข้องของการโทร (เรียกว่า A) ...

ดังที่ได้กล่าวไว้แล้วการเปลี่ยนtemplate<typename T1, int SIZE>เป็นการtemplate<typename T1, size_t SIZE>แก้ไขปัญหาที่คุณเห็น ตามที่ระบุไว้ใน[temp.deduct.call]/4คอมไพเลอร์ที่กำลังมองหาที่จะอนุมานที่เป็นเหมือนA Aตั้งแต่std::arrayมีการขัดแย้งแม่แบบ<class T, size_t N>(จาก[array.syn]) ก็พารามิเตอร์ที่สองคือในความเป็นจริงไม่ได้size_tint

ดังนั้นสำหรับการหักแม่แบบฟังก์ชั่นทั่วไปที่คุณtemplate<typename T1>สามารถที่จะตรงกับว่าประเภทของAที่ฐานะของผู้เชี่ยวชาญtemplate<typename T1, int SIZE>ไม่ได้เป็นที่แน่นอนการแข่งขัน ฉันเชื่อว่า MSVC ไม่ถูกต้องในการหักเงิน

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