การหักอาร์กิวเมนต์เทมเพลตสำหรับอาร์กิวเมนต์ของประเภทฟังก์ชั่น


10

พิจารณาโปรแกรมต่อไปนี้

#include <iostream>

template <typename T>
void f( void ( *fn )( T ) )
{
    fn( 42 );
}

void g( int x )
{
    std::cout << "g( " << x << " );\n";
}

int main()
{
    f( g );
}

โปรแกรมคอมไพล์ได้สำเร็จและผลลัพธ์คือ

g( 42 );

ตอนนี้ขอเปลี่ยนชื่อฟังก์ชั่นที่ไม่ใช่แม่แบบเพื่อgf

#include <iostream>

template <typename T>
void f( void ( *fn )( T ) )
{
    fn( 42 );
}

void f( int x )
{
    std::cout << "f( " << x << " );\n"; 
}

int main()
{
    f( f );
}

ตอนนี้โปรแกรมไม่ได้ถูกคอมไพล์ด้วย gcc HEAD 10.0.0 20200 และ clang HEAD 10.0.0 แต่เรียบเรียงโดย Visual C ++ 2019 เรียบร้อยแล้ว

ตัวอย่างเช่นคอมไพเลอร์ gcc ออกชุดข้อความต่อไปนี้

prog.cc: In function 'int main()':
prog.cc:22:10: error: no matching function for call to 'f(<unresolved overloaded function type>)'
   22 |     f( f );
      |          ^
prog.cc:4:6: note: candidate: 'template<class T> void f(void (*)(T))'
    4 | void f( void ( *fn )( T ) )
      |      ^
prog.cc:4:6: note:   template argument deduction/substitution failed:
prog.cc:22:10: note:   couldn't deduce template parameter 'T'
   22 |     f( f );
      |          ^
prog.cc:14:6: note: candidate: 'void f(int)'
   14 | void f( int x )
      |      ^
prog.cc:14:13: note:   no known conversion for argument 1 from '<unresolved overloaded function type>' to 'int'
   14 | void f( int x )
      |         ~~~~^

ดังนั้นจึงมีคำถามเกิดขึ้น: ควรรวบรวมรหัสและเหตุผลที่รหัสไม่รวบรวมโดย gcc และเสียงดังกราวคืออะไร?



หมายเหตุ: ในตัวอย่างแรกการผ่านg(แทน&g) ไปยังเทมเพลตฟังก์ชันทำให้เกิดการสลายประเภท (การอ้างอิงฟังก์ชัน lvalue จะสลายตัวไปยังตัวชี้ไปยังฟังก์ชัน: void(&)(T)=> void(*)(T)) การแปลงโดยนัยนี้เกิดขึ้นเนื่องจากไม่มีfโอเวอร์โหลดอื่นที่มีการจับคู่ที่ดีกว่า ในตัวอย่างที่สองมีความกำกวมที่fคุณต้องการโทรจริงเพราะ ... ไม่รู้ว่าสิ่งใดfเป็นอาร์กิวเมนต์เช่นกัน
Xeverous

คำตอบ:


7

สำหรับฉันดูเหมือนว่า gcc และ clang นั้นถูกต้อง สิ่งนี้ไม่ควรรวบรวม พารามิเตอร์ฟังก์ชั่นที่คุณต้องการTจะอนุมานได้กลายเป็นบริบทที่ไม่ได้อนุมานที่นี่ในขณะที่อาร์กิวเมนต์ที่ให้มาคือชุดโอเวอร์โหลดที่มีแม่แบบฟังก์ชั่น[temp.deduct.type] /5.5 :

บริบทที่ไม่อนุมานคือ:

  • [ ... ]
  • พารามิเตอร์ฟังก์ชันที่ไม่สามารถทำการหักอาร์กิวเมนต์ได้เนื่องจากอาร์กิวเมนต์ฟังก์ชันที่เกี่ยวข้องคือฟังก์ชันหรือชุดของฟังก์ชันที่โอเวอร์โหลด ([over.over]) และใช้อย่างน้อยหนึ่งอย่างต่อไปนี้:

    • [ ... ]
    • ชุดฟังก์ชั่นที่ให้มาเป็นอาร์กิวเมนต์มีหนึ่งหรือมากกว่าหนึ่งแม่แบบฟังก์ชั่น
  • [ ... ]

ดังนั้นTไม่สามารถอนุมานได้และโอเวอร์โหลดอื่น ๆ จะไม่สามารถใช้งานได้เนื่องจากไม่มีการแปลง สิ่งที่ gcc พูดว่า ...


0

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

void f( void ( *fn )( int ) ){
  fn( 42 );
}
void f( int x ){
    std::cout << "f( " << x << " );\n";
  }

  int main(){

     f( f );
  }

ฟังก์ชั่นที่ไม่ใช่แม่แบบจะต้องการเฉพาะเมื่อมีอย่างอื่นเสมอกันในระหว่างการแก้ปัญหาการโอเวอร์โหลด รหัสของคำถามไม่ได้มาถึงจุดนั้น: ไม่เคยมีความเชี่ยวชาญที่void f<int>(void(int))สร้างขึ้นเพื่อทำการแก้ปัญหาการโอเวอร์โหลดในแต่ละระดับ
Davis Herring
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.