Saya tahu bahwa kode di bawah ini adalah spesialisasi sebagian dari sebuah kelas:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
Saya juga tahu bahwa C ++ tidak mengizinkan spesialisasi sebagian template fungsi (hanya penuh yang diperbolehkan). Tetapi apakah kode saya berarti bahwa saya telah mengkhususkan sebagian template fungsi saya untuk satu / jenis argumen yang sama? Karena berfungsi untuk Microsoft Visual Studio 2010 Express! Jika tidak, dapatkah Anda menjelaskan konsep spesialisasi parsial?
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T1, typename T2>
inline T1 max (T1 const& a, T2 const& b)
{
return a < b ? b : a;
}
template <typename T>
inline T const& max (T const& a, T const& b)
{
return 10;
}
int main ()
{
cout << max(4,4.2) << endl;
cout << max(5,5) << endl;
int z;
cin>>z;
}
max(5,5)
menyelesaikannyamax(T const&, T const&) [with T=int]
dan tidakmax(T1 const&, T2 const&) [with T1=int and T2=int]
?Jawaban:
Spesialisasi parsial fungsi belum diperbolehkan sesuai standar. Dalam contoh ini, Anda benar-benar overloading & tidak mengkhususkan diri pada
max<T1,T2>
fungsi.Its sintaks harus melihat agak seperti di bawah ini, telah itu telah diizinkan:
// Partial specialization is not allowed by the spec, though! template <typename T> inline T const& max<T,T> (T const& a, T const& b) { ^^^^^ <--- [supposed] specializing here return 10; }
Dalam kasus template fungsi, hanya spesialisasi penuh yang diperbolehkan oleh standar C ++, - tidak termasuk ekstensi compiler!
sumber
(T, T)
over(T1, T2)
for(int, int)
, adalah karena yang pertama menjamin bahwa ada 2 parameter dan kedua tipe adalah sama; yang terakhir hanya menjamin bahwa ada 2 parameter. Penyusun selalu memilih deskripsi yang akurat. mis. Jika Anda harus membuat pilihan antara 2 deskripsi "River", mana yang akan Anda pilih? "kumpulan air" vs "kumpulan air yang mengalir".Karena spesialisasi parsial tidak diperbolehkan - seperti yang ditunjukkan oleh jawaban lain -, Anda dapat mengatasinya dengan menggunakan
std::is_same
danstd::enable_if
, seperti di bawah ini:template <typename T, class F> inline typename std::enable_if<std::is_same<T, int>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with ints! " << f << std::endl; } template <typename T, class F> inline typename std::enable_if<std::is_same<T, float>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with floats! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); }
Keluaran:
$ ./a.out >>> messing with ints! works >>> messing with floats! 2
Sunting : Jika Anda ingin menangani semua kasus lain yang tersisa, Anda dapat menambahkan definisi yang menyatakan bahwa kasus yang sudah ditangani tidak boleh cocok - jika tidak, Anda akan jatuh ke dalam definisi yang ambigu. Definisi tersebut dapat berupa:
template <typename T, class F> inline typename std::enable_if<(not std::is_same<T, int>::value) and (not std::is_same<T, float>::value), void>::type typed_foo(const F& f) { std::cout << ">>> messing with unknown stuff! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); typed_foo<std::string>("either"); }
Yang menghasilkan:
$ ./a.out >>> messing with ints! works >>> messing with floats! 2 >>> messing with unknown stuff! either
Meskipun hal semua kasus ini terlihat agak membosankan, karena Anda harus memberi tahu compiler semua yang telah Anda lakukan, cukup dapat dilakukan untuk menangani hingga 5 atau beberapa spesialisasi lagi.
sumber
Jika Anda benar-benar ingin memahami template, Anda harus melihat bahasa fungsional. Dunia template di C ++ adalah subbahasanya sendiri yang berfungsi murni.
Dalam bahasa fungsional, pemilihan dilakukan menggunakan Pencocokan Pola :
-- An instance of Maybe is either nothing (None) or something (Just a) -- where a is any type data Maybe a = None | Just a -- declare function isJust, which takes a Maybe -- and checks whether it's None or Just isJust :: Maybe a -> Bool -- definition: two cases (_ is a wildcard) isJust None = False isJust Just _ = True
Seperti yang Anda lihat, kami membebani definisi
isJust
.Nah, template kelas C ++ bekerja dengan cara yang persis sama. Anda memberikan deklarasi utama , yang menyatakan jumlah dan sifat parameter. Ini bisa berupa deklarasi, atau juga bertindak sebagai definisi (pilihan Anda), dan kemudian Anda dapat (jika Anda mau) memberikan spesialisasi pola dan mengaitkannya dengan mereka versi kelas yang berbeda (jika tidak maka akan konyol). .
Untuk fungsi templat, spesialisasi agak lebih aneh: ini agak bertentangan dengan resolusi yang berlebihan. Dengan demikian, telah diputuskan bahwa spesialisasi akan terkait dengan versi non-khusus, dan spesialisasi tidak akan dipertimbangkan selama resolusi kelebihan beban. Oleh karena itu, algoritma pemilihan fungsi yang tepat menjadi:
(untuk perawatan mendalam, lihat GotW # 49 )
Dengan demikian, spesialisasi templat fungsi adalah warga zona kedua (secara harfiah). Sejauh yang saya ketahui, kita akan lebih baik tanpanya: Saya belum pernah menemukan kasus di mana penggunaan spesialisasi template tidak dapat diselesaikan dengan kelebihan beban.
Tidak, ini hanya kelebihan beban, dan ini bagus. Faktanya, kelebihan beban biasanya bekerja seperti yang kita harapkan, sementara spesialisasi bisa mengejutkan (ingat artikel GotW yang saya tautkan).
sumber
"As such, template specialization of functions is a second-zone citizen (literally). As far as I am concerned, we would be better off without them: I have yet to encounter a case where a template specialization use could not be solved with overloading instead."
Bagaimana dengan parameter template non tipe?boost::mpl::integral_c<unsigned, 3u>
. Solusi lain juga bisa dengan menggunakanenable_if
/disable_if
, meskipun ceritanya berbeda.Spesialisasi parsial non-kelas dan non-variabel tidak diperbolehkan, tetapi seperti yang dikatakan:
Menambahkan kelas untuk meneruskan pemanggilan fungsi dapat menyelesaikan ini, berikut adalah contohnya:
template <class Tag, class R, class... Ts> struct enable_fun_partial_spec; struct fun_tag {}; template <class R, class... Ts> constexpr R fun(Ts&&... ts) { return enable_fun_partial_spec<fun_tag, R, Ts...>::call( std::forward<Ts>(ts)...); } template <class R, class... Ts> struct enable_fun_partial_spec<fun_tag, R, Ts...> { constexpr static R call(Ts&&... ts) { return {0}; } }; template <class R, class T> struct enable_fun_partial_spec<fun_tag, R, T, T> { constexpr static R call(T, T) { return {1}; } }; template <class R> struct enable_fun_partial_spec<fun_tag, R, int, int> { constexpr static R call(int, int) { return {2}; } }; template <class R> struct enable_fun_partial_spec<fun_tag, R, int, char> { constexpr static R call(int, char) { return {3}; } }; template <class R, class T2> struct enable_fun_partial_spec<fun_tag, R, char, T2> { constexpr static R call(char, T2) { return {4}; } }; static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, ""); static_assert(fun<int>(1, 1) == 2, ""); static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, ""); static_assert(fun<char>(1, 1) == 2, ""); static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, ""); static_assert(fun<long>(1L, 1L) == 1, ""); static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, ""); static_assert(fun<double>(1L, 1L) == 1, ""); static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, ""); static_assert(fun<int>(1u, 1) == 0, ""); static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, ""); static_assert(fun<char>(1, 'c') == 3, ""); static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, ""); static_assert(fun<unsigned>('c', 1) == 4, ""); static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, ""); static_assert(fun<unsigned>(10.0, 1) == 0, ""); static_assert( std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, ""); static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, ""); static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, ""); static_assert(fun<unsigned>() == 0, "");
sumber
Tidak. Misalnya, Anda dapat mengkhususkan secara hukum
std::swap
, tetapi Anda tidak dapat secara hukum menentukan kelebihan beban Anda sendiri. Itu berarti Anda tidak dapat membuatstd::swap
pekerjaan untuk template kelas kustom Anda sendiri.Spesialisasi yang berlebihan dan parsial dapat memiliki efek yang sama dalam beberapa kasus, tetapi jauh dari semuanya.
sumber
swap
kelebihan Anda di namespace Anda.Jawaban terlambat, tetapi beberapa pembaca yang terlambat mungkin menganggapnya berguna: Kadang-kadang, fungsi penolong - yang dirancang sedemikian rupa sehingga dapat dikhususkan - dapat menyelesaikan masalah juga.
Jadi mari kita bayangkan, inilah yang kami coba selesaikan:
template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = new R(x); f(r, y); // another template function? } // for some reason, we NEED the specialization: template <typename R, typename Y> void function<R, int, Y>(int x, Y y) { // unfortunately, Wrapper has no constructor accepting int: Wrapper* w = new Wrapper(); w->setValue(x); f(w, y); }
Oke, spesialisasi fungsi template parsial, kita tidak bisa melakukan itu ... Jadi mari kita "mengekspor" bagian yang diperlukan untuk spesialisasi ke dalam fungsi pembantu, mengkhususkan yang satu itu dan menggunakannya:
template <typename R, typename T> R* create(T t) { return new R(t); } template <> Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal... { Wrapper* w = new Wrapper(); w->setValue(n); return w; } template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = create<R>(x); f(r, y); // another template function? }
Ini bisa menjadi menarik terutama jika alternatifnya (kelebihan beban normal daripada spesialisasi, solusi yang diusulkan oleh Rubens, ... - bukan berarti ini buruk atau milik saya lebih baik, hanya satu lagi ) akan berbagi cukup banyak kode umum.
sumber