Apakah lambdifikasi suatu konsep merupakan perbaikan atau praktik yang buruk?
Tampaknya Anda dapat memasukkan lambda ke dalam konsep dan kemudian menulis kode di dalamnya. Mari kita ambil ini sebagai contoh. Saya akan lebih memilih konsep standar untuk konsep semacam itu dan ingatlah bahwa ini hanya untuk tujuan contoh ini - lambang pintu
template<class T>
concept labdified_concept =
requires {
[](){
T t, tt; // default constructible
T ttt{t}; // copy constructible
tt = t; //copy assignable
tt = std::move(t); // move assignable
};
};
Dari pada:
template<class T>
concept normal_concept =
std::default_initializable<T> && std::movable<T> && std::copy_constructible<T>;
Apakah lambdification merupakan perbaikan atau praktik yang buruk? Dari titik keterbacaan juga.
Jawaban
Ini seharusnya tidak valid. Inti dari lambda yang diizinkan ke dalam konteks yang tidak dievaluasi bukanlah untuk tiba-tiba mengizinkan SFINAE pada pernyataan.
Kami memiliki beberapa kata dalam [temp.deduct] / 9 yang menjelaskan hal ini:
Sebuah lambda ekspresi muncul dalam jenis fungsi atau parameter template tidak dianggap sebagai bagian dari konteks langsung untuk tujuan argumen template deduksi. [ Catatan : Tujuannya adalah untuk menghindari penerapan yang diperlukan untuk menangani kegagalan substitusi yang melibatkan pernyataan arbitrer. [ Contoh :
template <class T> auto f(T) -> decltype([]() { T::invalid; } ()); void f(...); f(0); // error: invalid expression not part of the immediate context template <class T, std::size_t = sizeof([]() { T::invalid; })> void g(T); void g(...); g(0); // error: invalid expression not part of the immediate context template <class T> auto h(T) -> decltype([x = T::invalid]() { }); void h(...); h(0); // error: invalid expression not part of the immediate context template <class T> auto i(T) -> decltype([]() -> typename T::invalid { }); void i(...); i(0); // error: invalid expression not part of the immediate context template <class T> auto j(T t) -> decltype([](auto x) -> decltype(x.invalid) { } (t)); // #1 void j(...); // #2 j(0); // deduction fails on #1, calls #2
- contoh akhir ] - catatan akhir ]
Kami hanya tidak memiliki sesuatu yang setara untuk persyaratan. perilaku gcc benar-benar seperti yang Anda harapkan:
template <typename T> concept C = requires { []{ T t; }; };
struct X { X(int); };
static_assert(!C<X>); // ill-formed
Karena badan lambda berada di luar konteks langsung, jadi ini bukan kegagalan substitusi, ini adalah kesalahan yang sulit.
Mengabaikan kelemahan keterbacaan yang jelas dalam mekanisme ini, itu tidak benar-benar berfungsi . Pertimbangkan hal berikut:
template<labdified_concept T>
void foo(T t) {}
template<typename T>
void foo(T t) {}
Aturan konsep memberi tahu kita bahwa jika suatu yang diberikan T
tidak memenuhi labdified_concept
, maka yang lain foo
harus dipakai sebagai gantinya. Tapi bukan itu yang terjadi jika kita menyediakan SS
template seperti itu. Sebaliknya, kami mendapatkan kesalahan keras karena labdified_concept<SS>
tidak dapat digunakan.
Hal-hal dalam requires
ekspresi memiliki penanganan khusus yang memungkinkan jenis kesalahan tertentu dianggap sebagai kegagalan untuk memenuhi persyaratan. Tetapi penanganan itu tidak berlaku untuk tubuh lambda. Ada, kode sakit-terbentuk sakit-dibentuk dan dengan demikian Anda mendapatkan error kompilasi ketika mencoba untuk menurunkannya.
Dan bahkan jika berhasil, tetap saja tidak berhasil. Konsep memiliki aturan yang kompleks untuk subsumsi konsep, yang memungkinkan berbagai konsep dianggap lebih terspesialisasi daripada yang lain. Hal ini memungkinkan kelebihan beban pada konsep yang berbeda , yang memungkinkan konsep yang lebih terbatas dipanggil. Misalnya konsep yang hanya membutuhkan default_initializable
lebih umum daripada yang membutuhkan default_initializable
dan moveable
. Jadi, jika suatu tipe memenuhi keduanya, yang terakhir akan diambil karena lebih dibatasi.
Tetapi ini hanya berfungsi karena aturan khusus untuk concept
s. Menyembunyikan persyaratan di lambda tidak akan memungkinkan ini berfungsi.