Saya baru-baru ini mengikuti diskusi Reddit yang mengarah ke perbandingan yang bagus dari std::visit
optimasi di kompiler. Saya perhatikan hal berikut: https://godbolt.org/z/D2Q5ED
Baik GCC9 dan Clang9 (saya kira mereka berbagi stdlib yang sama) tidak menghasilkan kode untuk memeriksa dan melempar pengecualian yang tidak bernilai ketika semua jenis memenuhi beberapa persyaratan. Ini mengarah ke codegen cara yang lebih baik, maka saya mengangkat masalah dengan MSVC STL dan disajikan dengan kode ini:
template <class T>
struct valueless_hack {
struct tag {};
operator T() const { throw tag{}; }
};
template<class First, class... Rest>
void make_valueless(std::variant<First, Rest...>& v) {
try { v.emplace<0>(valueless_hack<First>()); }
catch(typename valueless_hack<First>::tag const&) {}
}
Klaimnya adalah, bahwa ini membuat varian tidak bernilai, dan membaca dokumen itu harus:
Pertama, hancurkan nilai yang saat ini terkandung (jika ada). Kemudian langsung-menginisialisasi nilai yang terkandung seolah-olah membangun nilai tipe
T_I
dengan argumenstd::forward<Args>(args)....
Jika pengecualian dilemparkan,*this
dapat menjadi valueless_by_exception.
Apa yang saya tidak mengerti: Mengapa itu dinyatakan sebagai "mungkin"? Apakah sah untuk tetap di negara lama jika seluruh operasi dilemparkan? Karena inilah yang dilakukan GCC:
// For suitably-small, trivially copyable types we can create temporaries
// on the stack and then memcpy them into place.
template<typename _Tp>
struct _Never_valueless_alt
: __and_<bool_constant<sizeof(_Tp) <= 256>, is_trivially_copyable<_Tp>>
{ };
Dan kemudian (kondisional) melakukan sesuatu seperti:
T tmp = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);
Oleh karena itu pada dasarnya itu menciptakan sementara, dan jika itu berhasil menyalin / memindahkannya ke tempat nyata.
IMO ini merupakan pelanggaran "Pertama, hancurkan nilai yang saat ini terkandung" sebagaimana dinyatakan oleh dokumen. Ketika saya membaca standar, maka setelah v.emplace(...)
nilai saat ini dalam varian selalu dihancurkan dan tipe baru adalah tipe yang ditetapkan atau tidak berharga.
Saya mendapatkan bahwa kondisi is_trivially_copyable
mengecualikan semua jenis yang memiliki destruktor yang dapat diamati. Jadi ini bisa juga sebagai: "varian as-if diinisialisasi ulang dengan nilai lama" atau lebih. Tetapi keadaan varian adalah efek yang dapat diamati. Jadi apakah standar memang memungkinkan, yang emplace
tidak mengubah nilai saat ini?
Edit sebagai respons terhadap kutipan standar:
Kemudian menginisialisasi nilai yang terkandung seolah-olah langsung-non-daftar-inisialisasi nilai tipe TI dengan argumen
std::forward<Args>(args)...
.
Apakah T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
benar-benar dihitung sebagai implementasi yang valid dari hal di atas? Apakah ini yang dimaksud dengan "seolah-olah"?
might/may
kata - kata karena standar tidak menyatakan apa alternatifnya.