เหตุใดเทมเพลตฟังก์ชันจึงไม่สามารถใช้เฉพาะบางส่วนได้


89

ฉันทราบว่าข้อกำหนดภาษาห้ามมิให้มีความเชี่ยวชาญเฉพาะบางส่วนของเทมเพลตฟังก์ชัน

ฉันต้องการทราบเหตุผลว่าทำไมจึงห้าม? พวกเขาไม่มีประโยชน์?

template<typename T, typename U> void f() {}   //allowed!
template<> void f<int, char>()            {}   //allowed!
template<typename T> void f<char, T>()    {}   //not allowed!
template<typename T> void f<T, int>()     {}   //not allowed!

สำหรับtemplate<typename T, typename U> void f(T t, U u) {}ยัง template<> void f(int t, char u) {}จะได้รับอนุญาต
มืดมน

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

คำตอบ:


59

AFAIK ที่เปลี่ยนแปลงใน C ++ 0x

ฉันเดาว่ามันเป็นเพียงการกำกับดูแล (โดยพิจารณาว่าคุณสามารถรับเอฟเฟกต์ความเชี่ยวชาญเฉพาะบางส่วนพร้อมกับโค้ด verbose เพิ่มเติมได้โดยการวางฟังก์ชันเป็นstaticสมาชิกของคลาส)

คุณอาจค้นหา DR ที่เกี่ยวข้อง (รายงานข้อบกพร่อง) หากมี

แก้ไข : ตรวจสอบสิ่งนี้ฉันพบว่าคนอื่น ๆ ก็เชื่อเช่นนั้นเช่นกัน แต่ไม่มีใครสามารถค้นหาการสนับสนุนดังกล่าวในร่างมาตรฐานได้ กระทู้นี้จึงดูเหมือนว่าจะแสดงให้เห็นว่าเชี่ยวชาญบางส่วนของแม่แบบฟังก์ชั่นไม่ได้รับการสนับสนุนใน C

แก้ไข 2 : เป็นเพียงตัวอย่างของสิ่งที่ฉันหมายถึงโดย "วางฟังก์ชันเป็นstaticสมาชิกของคลาส":

#include <iostream>
using namespace std;

// template<typename T, typename U> void f() {}   //allowed!
// template<> void f<int, char>()            {}   //allowed!
// template<typename T> void f<char, T>()    {}   //not allowed!
// template<typename T> void f<T, int>()     {}   //not allowed!

void say( char const s[] ) { std::cout << s << std::endl; }

namespace detail {
    template< class T, class U >
    struct F {
        static void impl() { say( "1. primary template" ); }
    };

    template<>
    struct F<int, char> {
        static void impl() { say( "2. <int, char> explicit specialization" ); }
    };

    template< class T >
    struct F< char, T > {
        static void impl() { say( "3. <char, T> partial specialization" ); }
    };

    template< class T >
    struct F< T, int > {
        static void impl() { say( "4. <T, int> partial specialization" ); }
    };
}  // namespace detail

template< class T, class U >
void f() { detail::F<T, U>::impl(); }    

int main() {
    f<char const*, double>();       // 1
    f<int, char>();                 // 2
    f<char, double>();              // 3
    f<double, int>();               // 4
}

คุณมีมาตรฐานใน n3225 หรือไม่ ฉันค้นหาอย่างรวดเร็ว แต่ไม่พบ: /
Matthieu M.

1
อาขอโทษ ... ขาดคำ ฉันมีเอกสาร แต่ไม่พบย่อหน้านั้น แม้ว่าจะได้รับการแก้ไขของคุณฉันคิดว่ามันเป็นเพียงเพราะมันไม่ได้อยู่ในนั้น :)
Matthieu M.

3
สิ่งนี้ไม่มีการเปลี่ยนแปลงใน C ++ 0x ฉันยังสงสัยในประโยชน์ของมัน คุณสามารถโอเวอร์โหลดเทมเพลตและใช้ประโยชน์จากการสั่งซื้อบางส่วนได้ตลอดเวลา
Johannes Schaub - litb

1
การอัปเดตล่าช้า: ไม่เปลี่ยนแปลงแม้แต่ใน C ++ 17 แปดปีต่อมาและดูเหมือนจะไม่เข้าสู่ C ++ 20 ด้วย มองไม่เห็นเหตุผลใด ๆ แม้ว่า ...
Aconcagua

นี่เป็นการดำเนินการตามแนวคิดนี้อย่างครอบคลุมที่สุด
วิกเตอร์

19

คุณไม่สามารถทำเฉพาะฟังก์ชัน / วิธีการบางส่วนได้ แต่คุณสามารถทำโอเวอร์โหลดได้

template <typename T, typename U>
T fun(U pObj){...}

// acts like partial specialization <T, int> AFAIK 
// (based on Modern C++ Design by Alexandrescu)
template <typename T>
T fun(int pObj){...} 

มันเป็นวิธี แต่ฉันไม่รู้ว่ามันทำให้คุณพอใจหรือเปล่า


2
ว้าวใจของฉันเต็มไปด้วยแม่แบบที่ฉันลืมไปจริงๆว่าสิ่งที่เรียบง่ายเป็นไปได้อย่างไร :)
Johannes

2
น่าเสียดายที่ไม่ใช่กรณีที่คุณต้องการส่งผ่านอาร์กิวเมนต์
ตัวแปร

ฉันไม่แน่ใจว่าการส่งเทมเพลตตัวแปรหมายความว่าอย่างไรฉันจึงอยากทราบว่ามันแตกต่างจากความเชี่ยวชาญเฉพาะบางส่วนอย่างไร โปรดให้รายละเอียดเพิ่มเติมได้หรือไม่?
startpluses

จะเป็นอย่างไรถ้าคุณต้องการเพียงสองฟังก์ชันสำหรับประเภทอินทิกรัลและโฟลตทั้งหมด?
Dmitriy Dokshin

15

โดยทั่วไปไม่แนะนำให้เชี่ยวชาญเทมเพลตฟังก์ชันเลยเนื่องจากมีปัญหาในการใช้งานมากเกินไป นี่คือบทความดีๆจาก C / C ++ Users Journal: http://www.gotw.ca/publications/mill17.htm

และมีคำตอบที่ตรงไปตรงมาสำหรับคำถามของคุณ:

ประการหนึ่งคุณไม่สามารถเชี่ยวชาญบางส่วนได้ - ค่อนข้างมากเพราะภาษาบอกว่าคุณทำไม่ได้


3
บทความนี้ไม่เกี่ยวกับความเชี่ยวชาญเฉพาะบางส่วนนอกเหนือจากที่กล่าวถึงครั้งเดียว
Euri Pinhollow

11

เนื่องจากคุณสามารถเชี่ยวชาญชั้นเรียนได้บางส่วนคุณสามารถใช้ functor:

#include <iostream>

template < typename dtype , int k > struct fun
{
 int operator()()
 {
  return k ;
 }
} ;

template < typename dtype > struct fun < dtype , 0 >
{
 int operator()()
 {
  return 42 ;
 }
} ;

int main ( int argc , char * argv[] )
{
 std::cout << fun<float,5>()() << std::endl ;
 std::cout << fun<float,0>()() << std::endl ;
}

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