Saya mencoba mendapatkan contoh sederhana untuk bekerja guna memahami cara menggunakan std::enable_if
. Setelah saya membaca jawaban ini , saya pikir tidak terlalu sulit untuk memberikan contoh sederhana. Saya ingin menggunakan std::enable_if
untuk memilih antara dua fungsi anggota dan hanya memperbolehkan salah satunya digunakan.
Sayangnya, yang berikut ini tidak dikompilasi dengan gcc 4.7 dan setelah berjam-jam mencoba saya bertanya kepada kalian apa kesalahan saya.
#include <utility>
#include <iostream>
template< class T >
class Y {
public:
template < typename = typename std::enable_if< true >::type >
T foo() {
return 10;
}
template < typename = typename std::enable_if< false >::type >
T foo() {
return 10;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
gcc melaporkan masalah berikut:
% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x enable_if.cpp -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'
Mengapa g ++ tidak menghapus instantiasi yang salah untuk fungsi anggota kedua? Menurut standar, std::enable_if< bool, T = void >::type
hanya ada ketika parameter template boolean benar. Tetapi mengapa g ++ tidak menganggap ini sebagai SFINAE? Saya pikir bahwa pesan kesalahan kelebihan beban berasal dari masalah yang g ++ tidak menghapus fungsi anggota kedua dan percaya bahwa ini harus menjadi kelebihan beban.
std::is_same< T, int >::value
dan! std::is_same< T, int >::value
yang memberikan hasil yang sama.Jawaban:
SFINAE hanya berfungsi jika substitusi dalam pengurangan argumen dari argumen templat membuat konstruk menjadi buruk. Tidak ada substitusi seperti itu.
Itu karena ketika templat kelas instantiated (yang terjadi ketika Anda membuat objek bertipe di
Y<int>
antara kasus-kasus lain), templat kelas instantiate semua anggotanya (tidak harus definisi / badannya!). Di antara mereka ada juga templat anggotanya. Perhatikan yangT
diketahui saat itu, dan!std::is_same< T, int >::value
menghasilkan false. Jadi itu akan membuat kelasY<int>
yang berisiThe
std::enable_if<false>::type
mengakses-non ada jenis, sehingga deklarasi yang sakit-terbentuk. Dan dengan demikian program Anda tidak valid.Anda perlu membuat templat anggota
enable_if
bergantung pada parameter templat anggota itu sendiri. Maka deklarasi tersebut valid, karena seluruh tipe masih tergantung. Saat Anda mencoba memanggil salah satu dari mereka, pengurangan argumen untuk argumen templatnya terjadi dan SFINAE terjadi seperti yang diharapkan. Lihat pertanyaan ini dan jawaban yang sesuai tentang bagaimana melakukan itu.sumber
Y
kelas templat adalah instantiated, kompilator tidak akan benar-benar mengkompilasi fungsi anggota templat; Namun, kompiler AKAN melakukan substitusiT
ke dalam DECLARATIONS templat anggota sehingga templat anggota ini dapat dipakai di lain waktu. Titik kegagalan ini bukan SFINAE, karena SFINAE hanya berlaku saat menentukan sekumpulan fungsi yang mungkin untuk resolusi kelebihan beban , dan membuat instance kelas bukan merupakan kasus penentuan sekumpulan fungsi untuk resolusi kelebihan muatan. (Atau begitulah menurut saya!)Saya membuat contoh singkat ini yang juga berfungsi.
Komentar jika Anda ingin saya menguraikan. Saya pikir kode ini kurang lebih jelas, tetapi sekali lagi saya membuatnya jadi saya mungkin salah :)
Anda dapat melihatnya beraksi di sini .
sumber
error C4519: default template arguments are only allowed on a class template
.Q
, meskipun sama denganT
?test
fungsi anggota. Keduanya tidak bisa ada pada saat bersamaan.Q
hanya meneruskan tipe templat kelasT
. Anda dapat menghapus template kelasT
seperti: cpp.sh/4nxw tapi itu agak mengecewakan tujuannya.Bagi mereka yang terlambat datang yang mencari solusi yang "hanya bekerja":
Kompilasi dengan:
Menjalankan memberi:
sumber
std::enable_if_t
menjadiresolvedType
.Dari pos ini :
Tetapi orang dapat melakukan sesuatu seperti ini:
sumber
Salah satu cara untuk mengatasi masalah ini, spesialisasi fungsi anggota adalah menempatkan spesialisasi ke kelas lain, lalu mewarisi dari kelas itu. Anda mungkin harus mengubah urutan pewarisan untuk mendapatkan akses ke semua data mendasar lainnya tetapi teknik ini berhasil.
Kerugian dari teknik ini adalah bahwa jika Anda perlu menguji banyak hal yang berbeda untuk fungsi anggota yang berbeda Anda harus membuat kelas untuk masing-masing, dan rantai itu di pohon warisan. Ini berlaku untuk mengakses anggota data umum.
Ex:
sumber
Boolean perlu bergantung pada parameter template yang sedang dideduksi. Jadi cara mudah untuk memperbaikinya adalah dengan menggunakan parameter boolean default:
Namun, ini tidak akan berfungsi jika Anda ingin membebani fungsi anggota. Sebagai gantinya, yang terbaik untuk digunakan
TICK_MEMBER_REQUIRES
dari perpustakaan Tick :Anda juga dapat menerapkan anggota Anda sendiri membutuhkan makro seperti ini (kalau-kalau Anda tidak ingin menggunakan perpustakaan lain):
sumber
Ini adalah contoh minimalis saya, menggunakan makro. Gunakan tanda kurung ganda
enable_if((...))
saat menggunakan ekspresi yang lebih kompleks.sumber