Masalah bahasa yang aneh adalah CWG 1581 :
Ayat 15 [khusus] sangat jelas bahwa fungsi anggota khusus hanya didefinisikan secara implisit ketika mereka digunakan odr. Ini menciptakan masalah untuk ekspresi konstan dalam konteks yang tidak dievaluasi:
struct duration {
constexpr duration() {}
constexpr operator int() const { return 0; }
};
// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});
Masalahnya di sini adalah bahwa kita tidak diizinkan untuk mendefinisikan secara implisit constexpr duration::duration(duration&&)
dalam program ini, jadi ekspresi dalam daftar penginisialisasi bukan merupakan ekspresi konstan (karena memanggil fungsi constexpr yang belum didefinisikan), sehingga penginisialisasi bracing berisi konversi penyempitan , jadi programnya salah bentuk.
Jika kita batalkan komentar pada baris # 1, konstruktor pemindahan secara implisit didefinisikan dan program tersebut valid. Aksi seram di kejauhan ini sangat disayangkan. Implementasi berbeda pada titik ini.
Anda dapat membaca deskripsi masalah lainnya.
Resolusi untuk masalah ini diadopsi di P0859 di Albuquerque pada 2017 (setelah C ++ 17 dikirim). Masalah itu adalah pemblokir untuk keduanya dapat memiliki constexpr std::swap
(diselesaikan dalam P0879 ) dan constexpr std::invoke
(diselesaikan dalam P1065 , yang juga memiliki contoh CWG1581), baik untuk C ++ 20.
Contoh paling sederhana untuk dipahami di sini, menurut pendapat saya, adalah kode dari laporan bug LLVM yang ditunjukkan dalam P1065:
template<typename T>
int f(T x)
{
return x.get();
}
template<typename T>
constexpr int g(T x)
{
return x.get();
}
int main() {
// O.K. The body of `f' is not required.
decltype(f(0)) a;
// Seems to instantiate the body of `g'
// and results in an error.
decltype(g(0)) b;
return 0;
}
CWG1581 adalah tentang kapan fungsi anggota constexpr didefinisikan, dan resolusi memastikan bahwa mereka hanya ditentukan saat digunakan. Setelah P0859, di atas terbentuk dengan baik (tipe b
is int
).
Karena std::swap
dan std::invoke
keduanya harus bergantung pada memeriksa fungsi anggota (memindahkan konstruksi / penugasan di yang sebelumnya dan operator panggilan / panggilan pengganti di yang terakhir), keduanya bergantung pada resolusi masalah ini.
std::is_move_constructible_v<T> && std::is_move_assignable_v<T>
adalahtrue
. Itu tidak dapat terjadi jika fungsi anggota khusus belum dibuat.Alasannya
(karena @NathanOliver)
Untuk memungkinkan
constexpr
fungsi swap, Anda harus memeriksa - sebelum membuat contoh templat untuk fungsi ini - bahwa tipe yang ditukar adalah gerakan-konstruktif dan dapat ditetapkan-pindah. Sayangnya, karena cacat bahasa hanya diselesaikan dalam C ++ 20, Anda tidak dapat memeriksa itu, karena fungsi anggota yang relevan mungkin belum didefinisikan, sejauh menyangkut kompiler.Kronologi
<algorithm>
fungsi sebagaiconstexpr
.constexpr std::swap()
dan jugaconstexpr std::invoke()
- lihat penjelasan di atas.std::swap
dan beberapa konstruksi lainnya, dan ini diterima ke dalam C ++ 17.std::swap()
constexpr setelah resolusi CWG-1581.std::invoke()
perbaikan constexpr .Kasus spesifik Anda
Anda dapat menggunakan
constexpr
swapping jika Anda tidak memeriksa move-constructibility dan move-assignability, melainkan langsung memeriksa beberapa fitur tipe lain yang memastikannya. mis. hanya tipe primitif dan tidak ada kelas atau struct. Atau, secara teoritis, Anda bisa melepaskan cek dan hanya berurusan dengan kesalahan kompilasi yang mungkin Anda temui, dan dengan perilaku flaky beralih di antara kompiler. Bagaimanapun, jangan gantistd::swap()
dengan hal semacam itu.sumber