Ketika saya mencoba untuk menggunakan float
sebagai parameter template, kompilator meminta kode ini, sementara int
berfungsi dengan baik.
Apakah karena saya tidak dapat menggunakan float
sebagai parameter template?
#include<iostream>
using namespace std;
template <class T, T defaultValue>
class GenericClass
{
private:
T value;
public:
GenericClass()
{
value = defaultValue;
}
T returnVal()
{
return value;
}
};
int main()
{
GenericClass <int, 10> gcInteger;
GenericClass < float, 4.6f> gcFlaot;
cout << "\n sum of integer is "<<gcInteger.returnVal();
cout << "\n sum of float is "<<gcFlaot.returnVal();
return 0;
}
Kesalahan:
main.cpp: In function `int main()':
main.cpp:25: error: `float' is not a valid type for a template constant parameter
main.cpp:25: error: invalid type in declaration before ';' token
main.cpp:28: error: request for member `returnVal' in `gcFlaot',
which is of non-class type `int'
Saya membaca "Struktur Data untuk Pemrogram Game" oleh Ron Penton, penulis melewati a float
, tetapi ketika saya mencobanya sepertinya tidak dapat dikompilasi.
c++
templates
generics
floating-point
yokks
sumber
sumber
float
sebagai parameter template non-tipe ? Dalam bab apakah itu?Jawaban:
Standar C ++ saat ini tidak mengizinkan
float
(yaitu bilangan real) atau literal string karakter untuk digunakan sebagai parameter non-tipe template . Anda tentu saja dapat menggunakanfloat
danchar *
types sebagai argumen biasa.Mungkin penulis menggunakan kompilator yang tidak mengikuti standar saat ini?
sumber
template<char ...cs>
, maka string literal dapat diubah menjadi paket tersebut pada waktu kompilasi. Berikut ini demo tentang ideone . (Demo adalah C ++ 14, tetapi mudah untuk memindahkannya kembali ke C ++ 11 -std::integer_sequence
adalah satu-satunya kesulitan)char &*
sebagai parameter template jika Anda mendefinisikan literal di tempat lain. Bekerja dengan cukup baik sebagai solusi.JAWABAN SEDERHANA
Standar tidak mengizinkan floating point sebagai non-type template-arguments , yang dapat dibaca di bagian C ++ 11 standar berikut;
Tapi .. tapi .. KENAPA !?
Mungkin karena fakta bahwa perhitungan floating point tidak dapat disajikan dengan tepat. Jika diizinkan, hal itu dapat / akan menghasilkan perilaku yang keliru / aneh ketika melakukan sesuatu seperti ini;
Kami bermaksud memanggil fungsi yang sama dua kali tetapi ini mungkin tidak terjadi karena representasi floating point dari dua penghitungan tidak dijamin akan persis sama.
Bagaimana saya merepresentasikan nilai floating point sebagai argumen template?
Dengan
C++11
Anda bisa menulis beberapa ekspresi-konstan yang cukup canggih ( constexpr ) yang yang akan menghitung pembilang / penyebut dari waktu kompilasi nilai mengambang dan kemudian meneruskan keduanya sebagai argumen integer terpisah.Ingatlah untuk menentukan semacam ambang batas sehingga nilai titik mengambang yang dekat satu sama lain menghasilkan pembilang / penyebut yang sama , jika tidak, itu agak tidak berguna karena kemudian akan menghasilkan hasil yang sama yang disebutkan sebelumnya sebagai alasan untuk tidak mengizinkan nilai titik mengambang sebagai bukan tipe argumen template .
sumber
<ratio>
, dijelaskan oleh §20.10 sebagai "aritmatika rasional waktu kompilasi". Yang tepat untuk contoh Anda.<ratio>
?12345 * 12345
? (Ini tidak memungkinkanint
template parameter meskipun tidak menentukan lebar int ditandatangani atau apakah ekspresi yang UB.)Hanya untuk memberikan salah satu alasan mengapa ini menjadi batasan (setidaknya dalam standar saat ini).
Saat mencocokkan spesialisasi template, kompilator mencocokkan argumen template, termasuk argumen non-tipe.
Pada dasarnya, nilai floating point tidak tepat dan implementasinya tidak ditentukan oleh standar C ++. Akibatnya, sulit untuk memutuskan kapan dua argumen non-tipe floating point benar-benar cocok:
Ekspresi ini tidak selalu menghasilkan "pola bit" yang sama dan oleh karena itu tidak mungkin untuk menjamin bahwa mereka menggunakan spesialisasi yang sama - tanpa kata-kata khusus untuk menutupi ini.
sumber
==
operator :-) Kami sudah menerima ketidakakuratan ini saat runtime, mengapa tidak pada waktu kompilasi juga?Memang, Anda tidak dapat menggunakan literal float sebagai parameter template. Lihat bagian 14.1 ("Parameter template non-tipe harus memiliki salah satu dari tipe berikut (secara opsional memenuhi kualifikasi cv) ...") dari standar.
Anda dapat menggunakan referensi ke float sebagai parameter template:
sumber
Bungkus parameter di kelasnya sendiri sebagai konsteks. Secara efektif ini mirip dengan sifat karena parameterisasi kelas dengan satu set float.
dan kemudian buat template dengan jenis kelas sebagai parameter
dan kemudian gunakan seperti itu ...
Hal ini memungkinkan compiler untuk menjamin bahwa hanya satu contoh kode yang dibuat untuk setiap contoh template dengan paket parameter yang sama. Itu mengatasi semua masalah dan Anda bisa menggunakan float dan doubles sebagai constexpr di dalam kelas templated.
sumber
Jika Anda tidak masalah memiliki default tetap per tipe, Anda dapat membuat tipe untuk mendefinisikannya sebagai konstanta dan mengkhususkan sesuai kebutuhan.
Jika Anda memiliki C ++ 11, Anda bisa menggunakan constexpr saat menentukan nilai default. Dengan C ++ 14, MyTypeDefault dapat menjadi variabel template yang sedikit lebih bersih secara sintaks.
sumber
Jawaban lain memberikan alasan bagus mengapa Anda mungkin tidak menginginkan parameter template floating point, tetapi IMO braker real deal adalah bahwa persamaan menggunakan '==' dan persamaan bitwise tidak sama:
-0.0 == 0.0
, tetapi0.0
dan-0.0
tidak sama dengan bitwiseNAN != NAN
Tidak ada jenis persamaan yang merupakan pembatalan yang baik untuk persamaan jenis: Tentu saja, poin 2. membuat penggunaan
==
tidak valid untuk menentukan persamaan jenis. Seseorang dapat menggunakan persamaan bitwise sebagai gantinya, tetapi kemudianx != y
tidak menyiratkannyaMyClass<x>
danMyClass<y>
merupakan tipe yang berbeda (dengan 2.), yang akan agak aneh.sumber
Anda selalu bisa berpura-pura ...
Ref: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
sumber
float
! = Bilangan rasional. Keduanya adalah gagasan yang sangat terpisah. Satu dihitung melalui mantissa & eksponen, yang lainnya adalah rasional - tidak setiap nilai yang dapat diwakili oleh rasional dapat diwakili oleh afloat
.float
jelas merupakan bilangan rasional, tetapi adafloat
s yang tidak dapat direpresentasikan sebagai rasio duaint
s. Mantissa adalah Integer, dan eksponen 2 ^ adalah IntegerJika Anda tidak memerlukan double untuk menjadi konstanta waktu kompilasi, Anda dapat meneruskannya sebagai pointer:
sumber
Dimulai dengan C ++ 20 hal ini dimungkinkan .
Ini juga memberikan jawaban atas pertanyaan awal:
Karena belum ada yang menerapkannya dalam standar. Tidak ada alasan mendasar.
Dalam C ++ 20 parameter template non-tipe sekarang dapat berupa float dan bahkan objek kelas.
Ada beberapa persyaratan pada objek kelas (harus berupa tipe literal ) dan memenuhi beberapa persyaratan lain untuk mengecualikan kasus patologis seperti operator yang ditentukan pengguna == ( Detail ).
Kami bahkan bisa menggunakan
auto
Perhatikan bahwa GCC 9 (dan 10) mengimplementasikan parameter template non-jenis kelas, tetapi belum untuk float .
sumber
Jika Anda hanya ingin merepresentasikan presisi tetap, Anda dapat menggunakan teknik seperti ini untuk mengubah parameter float menjadi int.
Misalnya larik dengan faktor pertumbuhan 1,75 dapat dibuat sebagai berikut dengan asumsi presisi 2 digit (bagi dengan 100).
Jika Anda tidak menyukai representasi 1,75 sebagai 175 dalam daftar argumen templat maka Anda selalu dapat membungkusnya dalam beberapa makro.
sumber
...::Factor = _Factor_/100.0;
sebaliknya itu akan menjadi divisi integer.