Saya telah melihat beberapa contoh C ++ menggunakan parameter templat templat (yaitu templat yang mengambil templat sebagai parameter) untuk melakukan desain kelas berbasis kebijakan. Apa kegunaan lain dari teknik ini?
c++
templates
template-templates
Ferruccio
sumber
sumber
Jawaban:
Saya pikir Anda perlu menggunakan sintaks templat templat untuk lulus parameter yang tipenya templat bergantung pada templat lain seperti ini:
Di sini,
H
adalah templat, tapi saya ingin fungsi ini menangani semua spesialisasiH
.CATATAN : Saya telah memprogram c ++ selama bertahun-tahun dan hanya membutuhkan ini sekali. Saya menemukan bahwa ini adalah fitur yang jarang diperlukan (tentu saja berguna ketika Anda membutuhkannya!).
Saya sudah mencoba memikirkan contoh yang baik, dan jujur, sebagian besar waktu ini tidak diperlukan, tetapi mari kita buat contoh. Mari kita berpura-pura
std::vector
tidak punyatypedef value_type
.Jadi bagaimana Anda menulis fungsi yang dapat membuat variabel dari tipe yang tepat untuk elemen vektor? Ini akan berhasil.
CATATAN :
std::vector
memiliki dua parameter templat, jenis, dan pengalokasi, jadi kami harus menerima keduanya. Untungnya, karena pengurangan tipe, kita tidak perlu menuliskan tipe yang tepat secara eksplisit.yang dapat Anda gunakan seperti ini:
atau lebih baik lagi, kita bisa menggunakan:
PEMBARUAN : Bahkan contoh yang dibuat-buat ini, walaupun bersifat ilustratif, bukan lagi contoh yang luar biasa karena pengenalan c ++ 11
auto
. Sekarang fungsi yang sama dapat ditulis sebagai:begitulah cara saya lebih suka menulis kode jenis ini.
sumber
template<template<class, class> class C, class T, class U> void f(C<T, U> &v)
f<vector,int>
dan tidakf<vector<int>>
.f<vector,int>
berartif<ATemplate,AType>
,f<vector<int>>
berartif<AType>
Sebenarnya, usecase untuk parameter templat templat agak jelas. Setelah Anda mengetahui bahwa C ++ stdlib memiliki lubang menganga tidak mendefinisikan operator keluaran aliran untuk jenis kontainer standar, Anda akan melanjutkan untuk menulis sesuatu seperti:
Maka Anda akan mengetahui bahwa kode untuk vektor adalah sama, untuk forward_list adalah sama, sebenarnya, bahkan untuk banyak jenis peta itu masih sama. Kelas-kelas template itu tidak memiliki kesamaan kecuali untuk meta-interface / protokol, dan menggunakan parameter template template memungkinkan untuk menangkap kesamaan di semua dari mereka. Sebelum melanjutkan untuk menulis templat, ada baiknya memeriksa referensi untuk mengingat bahwa wadah urutan menerima 2 argumen templat - untuk jenis nilai dan pengalokasi. Meskipun pengalokasi default, kita masih harus memperhitungkan keberadaannya di operator template kami <<:
Voila, yang akan bekerja secara otomatis untuk semua kontainer urutan sekarang dan masa depan mematuhi protokol standar. Untuk menambahkan peta ke dalam campuran, perlu mengintip referensi untuk mencatat bahwa mereka menerima 4 params templat, jadi kita membutuhkan versi lain dari operator << di atas dengan param templat templat 4-arg. Kami juga akan melihat bahwa std: pair mencoba dirender dengan operator 2-arg << untuk tipe urutan yang kami tentukan sebelumnya, jadi kami akan menyediakan spesialisasi hanya untuk std :: pair.
Btw, dengan C + 11 yang memungkinkan templat variadic (dan karenanya seharusnya mengizinkan templat templat variadic), mungkin saja memiliki operator tunggal << untuk mengatur semuanya. Sebagai contoh:
Keluaran
sumber
__PRETTY_FUNCTION__
, yang, antara lain, melaporkan deskripsi parameter templat dalam teks biasa. dentang melakukannya juga. Fitur yang paling berguna kadang-kadang (seperti yang Anda lihat).Berikut adalah contoh sederhana yang diambil dari 'Desain C ++ Modern - Pemrograman Generik dan Pola Desain yang Diterapkan' oleh Andrei Alexandrescu:
Dia menggunakan kelas dengan parameter templat templat untuk menerapkan pola kebijakan:
Dia menjelaskan: Biasanya, kelas host sudah tahu, atau dapat dengan mudah menyimpulkan, argumen templat dari kelas kebijakan. Dalam contoh di atas, WidgetManager selalu mengelola objek dari jenis Widget, sehingga mengharuskan pengguna untuk menentukan Widget lagi di Instansiasi dari CreationPolicy adalah berlebihan dan berpotensi berbahaya. Dalam hal ini, kode perpustakaan dapat menggunakan parameter templat templat untuk menentukan kebijakan.
Efeknya adalah bahwa kode klien dapat menggunakan 'WidgetManager' dengan cara yang lebih elegan:
Alih-alih cara yang lebih rumit, dan cenderung rawan bahwa definisi yang membutuhkan argumen template template:
sumber
Berikut adalah contoh praktis lain dari perpustakaan jaringan saraf CUDA Convolutional saya . Saya memiliki templat kelas berikut:
yang sebenarnya mengimplementasikan manipulasi matriks n-dimensi. Ada juga templat kelas anak:
yang mengimplementasikan fungsi yang sama tetapi dalam GPU. Kedua templat dapat bekerja dengan semua tipe dasar, seperti float, double, int, dll. Dan saya juga memiliki templat kelas (disederhanakan):
Alasan di sini untuk memiliki sintaks template templat adalah karena saya dapat mendeklarasikan implementasi kelas
yang akan memiliki bobot dan input tipe float dan pada GPU, tetapi connection_matrix akan selalu int, baik pada CPU (dengan menentukan TT = Tensor) atau pada GPU (dengan menentukan TT = TensorGPU).
sumber
Katakanlah Anda menggunakan CRTP untuk menyediakan "antarmuka" untuk satu set templat anak; dan kedua orang tua dan anak adalah parametrik dalam argumen templat lain:
Perhatikan duplikasi 'int', yang sebenarnya merupakan parameter tipe yang sama yang ditentukan untuk kedua templat. Anda dapat menggunakan templat templat untuk DERIVED untuk menghindari duplikasi ini:
Perhatikan bahwa Anda menghilangkan secara langsung menyediakan parameter templat lain ke turunan templat ; "antarmuka" masih menerimanya.
Ini juga memungkinkan Anda membangun typedef di "antarmuka" yang bergantung pada parameter tipe, yang akan dapat diakses dari templat yang diturunkan.
Typedef di atas tidak berfungsi karena Anda tidak dapat mengetik ke template yang tidak ditentukan. Ini berfungsi, namun (dan C ++ 11 memiliki dukungan asli untuk typedefs templat):
Sayangnya, Anda memerlukan satu turunan_interface_type untuk setiap contoh kerangka turunan, kecuali ada trik lain yang belum saya pelajari.
sumber
derived
dapat digunakan tanpa argumen templatnya, yaitu baristypedef typename interface<derived, VALUE> type;
template <typename>
. Dalam arti tertentu, Anda dapat menganggap parameter template memiliki 'metatype'; metatype normal untuk parameter templat adalahtypename
yang artinya harus diisi dengan tipe biasa; yangtemplate
berarti metatype perlu diisi dengan referensi ke template.derived
mendefinisikan templat yang menerima satutypename
parameter metatyped, sehingga sesuai dengan tagihan dan dapat dirujuk di sini. Masuk akal?typedef
. Selain itu, Anda dapat menghindari duplikatint
dalam contoh pertama Anda dengan menggunakan konstruk standar sepertivalue_type
pada tipe DERIVED.typedef
masalah dari blok 2. Tapi poin 2 valid saya pikir ... ya, itu mungkin akan menjadi cara yang lebih sederhana untuk melakukan hal yang sama.Inilah yang saya temui:
Dapat diatasi untuk:
atau (kode kerja):
sumber
Dalam solusi dengan templat variadic yang disediakan oleh pfalcon, saya merasa kesulitan untuk benar-benar mengkhususkan operator ostream untuk std :: map karena sifat serakah dari spesialisasi variadic. Inilah sedikit revisi yang berhasil bagi saya:
sumber
Ini salah satu yang digeneralisasi dari sesuatu yang baru saja saya gunakan. Saya mempostingnya karena ini adalah contoh yang sangat sederhana, dan menunjukkan penggunaan praktis bersama dengan argumen default:
sumber
Ini meningkatkan keterbacaan kode Anda, memberikan keamanan tipe tambahan dan menghemat beberapa upaya kompiler.
Katakanlah Anda ingin mencetak setiap elemen wadah, Anda dapat menggunakan kode berikut tanpa parameter templat templat
atau dengan parameter templat templat
Misalkan Anda memasukkan bilangan bulat, katakanlah
print_container(3)
. Untuk kasus sebelumnya, template akan dipakai oleh kompiler yang akan mengeluh tentang penggunaanc
dalam for for, yang terakhir tidak akan instantiate templat sama sekali karena tidak ada jenis pencocokan yang dapat ditemukan.Secara umum, jika kelas / fungsi templat Anda dirancang untuk menangani kelas templat sebagai parameter templat, lebih baik untuk membuatnya jelas.
sumber
Saya menggunakannya untuk tipe berversi.
Jika Anda memiliki tipe yang diversi melalui templat seperti
MyType<version>
, Anda bisa menulis fungsi yang bisa Anda ambil nomor versinya:Jadi Anda dapat melakukan hal-hal yang berbeda tergantung pada versi dari jenis yang diteruskan alih-alih memiliki kelebihan untuk masing-masing jenis. Anda juga dapat memiliki fungsi konversi yang menerima
MyType<Version>
dan mengembalikanMyType<Version+1>
, dengan cara yang umum, dan bahkan mengembalikannya untuk memilikiToNewest()
fungsi yang mengembalikan versi jenis terbaru dari versi yang lebih lama (sangat berguna untuk log yang mungkin telah disimpan beberapa waktu yang lalu) tetapi perlu diproses dengan alat terbaru hari ini).sumber