Parameter template non-jenis

93

Saya memahami bahwa parameter template bukan tipe harus berupa ekspresi integral yang konstan. Bisakah seseorang menjelaskan mengapa demikian?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

Saya mengerti apa itu ekspresi integral konstan. Apa alasan untuk tidak mengizinkan jenis tidak konstan seperti std::stringpada cuplikan di atas?

Mahesh
sumber
17
Parameter template diselesaikan pada waktu kompilasi.
Etienne de Martel

Jawaban:

121

Alasan Anda tidak dapat melakukan ini adalah karena ekspresi non-konstan tidak dapat diurai dan diganti selama waktu kompilasi. Mereka bisa berubah selama runtime, yang akan membutuhkan pembuatan template baru selama runtime, yang tidak mungkin dilakukan karena template adalah konsep waktu kompilasi.

Inilah yang diizinkan standar untuk parameter template non-tipe (14.1 [temp.param] p4):

Parameter template non-tipe harus memiliki salah satu dari tipe berikut (secara opsional memenuhi kualifikasi cv):

  • integral atau jenis pencacahan,
  • penunjuk ke objek atau penunjuk ke fungsi,
  • lvalue referensi ke objek atau lvalue referensi ke fungsi,
  • penunjuk ke anggota,
  • std::nullptr_t.
Xeo
sumber
6
@ ALOToverflow: Itu berada di bawah "pointer to member". Ini bisa berupa "penunjuk ke fungsi anggota" atau "penunjuk ke data anggota".
Xeo
4
Perlu dicatat bahwa untuk kasus pointer ke objek (atau bidang contoh), objek harus memiliki durasi penyimpanan statis dan tautan (eksternal pra C ++ 11, internal atau eksternal dalam C ++ 11), sehingga penunjuk ke mereka dapat dibuat pada waktu kompilasi.
Theodore Murdock
2
Dalam C ++ 20, hal ini sekarang diizinkan asalkan jenisnya memiliki kesamaan terstruktur yang kuat, merupakan subobjek literal, tidak ada yang bisa berubah / tidak stabil dan di mana operator pesawat ruang angkasa bersifat publik.
Rakete1111
73

Itu tidak diperbolehkan.

Namun, ini diperbolehkan:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

Lihat §14.1 / 6,7,8 dalam C ++ Standard (2003).


Ilustrasi:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

Keluaran:

can assign values locally
can assign values locally
can assign values locally...appended some string
Nawaz
sumber
7
@Mahesh: Karena yang pada dasarnya template template adalah alamat dari std::stringpointer atau objek referensi. Jika variabel itu lokal, Anda mungkin akan mendapatkan alamat yang berbeda setiap kali fungsi dipanggil.
Xeo
11
@Mahesh: Anda tidak tahu seperti apa tampilan tumpukan panggilan pada waktu kompilasi. Sebelum fungsi Anda, mungkin ada 10 lainnya atau 3 lainnya atau bagaimanapun banyak lainnya, sehingga alamat di tumpukan, di mana string dibuat dapat berubah dari panggilan ke panggilan. Ketika Anda memiliki objek dengan tautan eksternal, alamatnya diperbaiki selama kompilasi / tautan.
Xeo
2
@Xeo " alamatnya ditetapkan selama kompilasi / penautan. " Atau tidak, untuk kode yang dapat direlokasi atau tidak tergantung posisi.
penasaran
1
Jawaban ini (saat ini) tampaknya tidak menjawab pertanyaan OP, yang menanyakan mengapa perilaku ini ada; jawaban ini hanya menyatakan kembali contoh OP tanpa memberikan penjelasan apapun.
Quuxplusone
1
Saya terlambat ke pesta, tampaknya petunjuk cerdas juga tidak berfungsi
Nicholas Humphrey
28

Anda harus mampu mengatasi argumen template

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

Sekarang impl akan perlu muncul dengan urutan karakter yang unik untuk std::stringatau, dalam hal ini, kelas yang ditentukan pengguna sewenang-wenang lainnya, menyimpan nilai tertentu, yang artinya tidak diketahui oleh implementasi. Selain itu, nilai objek kelas arbitrer tidak dapat dihitung pada waktu kompilasi.

Direncanakan untuk mempertimbangkan mengizinkan jenis kelas literal sebagai jenis parameter template untuk post-C ++ 0x, yang diinisialisasi oleh ekspresi konstan. Mereka dapat dihancurkan dengan membuat anggota data secara rekursif dihancurkan sesuai dengan nilainya (untuk kelas dasar, misalnya kita dapat menerapkan depth-first, traversal kiri-ke-kanan). Tapi itu pasti tidak akan berhasil untuk kelas yang sewenang-wenang.

Johannes Schaub - litb
sumber
10

Argumen template non-tipe yang disediakan dalam daftar argumen template adalah ekspresi yang nilainya dapat ditentukan pada waktu kompilasi. Argumen seperti itu harus:

ekspresi konstan, alamat fungsi atau objek dengan hubungan eksternal, atau alamat anggota kelas statis.

Selain itu, literal string adalah objek dengan tautan internal, jadi Anda tidak dapat menggunakannya sebagai argumen template. Anda juga tidak dapat menggunakan penunjuk global. Literal floating-point tidak diperbolehkan, mengingat kemungkinan kesalahan pembulatan yang jelas.

Sadique
sumber
Anda menggunakan kutipan, jadi apa sumbernya? Saya ingin memberi suara positif jika memiliki sumber kutipan.
Gabriel Staples