Mengapa template fungsi tidak dapat dikhususkan sebagian?

89

Saya tahu spesifikasi bahasa melarang spesialisasi parsial dari template fungsi.

Saya ingin tahu alasan mengapa melarangnya? Apakah mereka tidak berguna?

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!
Nawaz
sumber
Untuk template<typename T, typename U> void f(T t, U u) {}juga template<> void f(int t, char u) {}diperbolehkan.
dashesy
10
Saya merasa menarik bahwa orang terus memberikan solusi ketika pertanyaannya bukan "bagaimana saya bisa mencapai tujuan yang sama" tetapi "apa alasan di balik perilaku ini" ... Saya sendiri tidak tahu alasan pilihan ini, tetapi saya berasumsi panitia pasti punya alasan untuk melarang spesialisasi sebagian template fungsi. Sejauh ini penjelasan "terdekat" adalah tautan yang diposting oleh Georgy, yang hanya menunjukkan potensi "risiko" dari spesialisasi sebagian template fungsi saat kelebihan beban ada. Namun, saya rasa itu bukan alasan untuk melarang fitur ini, jadi saya berasumsi ada yang lebih dari ini ..
bartgol

Jawaban:

59

AFAIK yang diubah di C ++ 0x.

Saya kira itu hanya kekeliruan (mengingat Anda selalu bisa mendapatkan efek spesialisasi parsial dengan kode yang lebih verbose, dengan menempatkan fungsi sebagai staticanggota kelas).

Anda dapat mencari DR (Laporan Cacat) yang relevan, jika ada.

EDIT : memeriksa ini, saya menemukan bahwa orang lain juga percaya itu, tetapi tidak ada yang dapat menemukan dukungan seperti itu dalam standar draf. Utas SO ini tampaknya menunjukkan bahwa spesialisasi sebagian dari templat fungsi tidak didukung di C ++ 0x .

EDIT 2 : hanya contoh dari apa yang saya maksud dengan "menempatkan fungsi sebagai staticanggota kelas":

#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
}
Cheers and hth. - Alf
sumber
apakah Anda memiliki standar n3225? Saya melakukan pencarian cepat tetapi tidak dapat menemukannya: /
Matthieu M.
1
ah maaf ... ada kata yang hilang. Saya memiliki dokumennya, tetapi tidak dapat menemukan paragraf tertentu . Meskipun diberi suntingan Anda, saya rasa itu hanya karena tidak ada di sana :)
Matthieu M.
3
Ini tidak diubah di C ++ 0x. Saya juga meragukan kegunaannya. Anda selalu dapat membebani template dan memanfaatkan pemesanan parsial .
Johannes Schaub - litb
1
Pembaruan terakhir: tidak berubah bahkan di C ++ 17 delapan tahun kemudian, dan sepertinya tidak memasuki C ++ 20 juga. Tidak dapat melihat alasan apa pun untuk ...
Aconcagua
Sejauh ini, ini adalah implementasi konsep yang paling komprehensif, saya yakin
Victor
19

Nah, Anda benar-benar tidak dapat melakukan spesialisasi fungsi / metode parsial namun Anda dapat melakukan overloading.

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){...} 

Ini adalah jalannya tetapi saya tidak tahu apakah itu memuaskan Anda.

Michal W.
sumber
2
Wow pikiran saya dipenuhi dengan template sehingga saya benar-benar lupa betapa sederhananya hal itu :)
Johannes
2
Sayangnya, tidak demikian halnya ketika Anda ingin melewatkan argumen variadik setelah sebagian mengkhususkan fungsi .. :(
Gwangmu Lee
Saya tidak yakin apa yang dimaksud dengan meneruskan template variadic, jadi saya ingin tahu bagaimana perbedaannya dari spesialisasi parsial. Bisakah Anda memberikan detail lebih lanjut?
beginpluses
Bagaimana jika Anda hanya menginginkan dua fungsi untuk semua tipe integral dan float?
Dmitriy Dokshin
15

Secara umum, tidak disarankan untuk mengkhususkan template fungsi sama sekali, karena ada masalah dengan kelebihan muatan. Berikut artikel bagus dari C / C ++ Users Journal: http://www.gotw.ca/publications/mill17.htm

Dan itu berisi jawaban jujur ​​atas pertanyaan Anda:

Untuk satu hal, Anda tidak dapat mengkhususkan mereka sebagian - cukup banyak hanya karena bahasanya mengatakan Anda tidak bisa.

Georgy Pashkov
sumber
3
Artikel ini bukan tentang spesialisasi parsial, selain itu disebutkan sekali.
Euri Pinhollow
11

Karena Anda dapat mengkhususkan sebagian kelas, Anda dapat menggunakan 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 ;
}
Kay F. Jahnke
sumber
1
Anda kemudian dapat menggunakan template fungsi tunggal untuk melakukan panggilan, menghilangkan ()()sintaks yang jelek .
tmr232