Gần đây tôi đã theo dõi một cuộc thảo luận Reddit dẫn đến một so sánh tốt về std::visit
tối ưu hóa giữa các trình biên dịch. Tôi nhận thấy như sau: https://godbolt.org/z/D2Q5ED
Cả GCC9 và Clang9 (tôi đoán rằng chúng có chung stdlib) không tạo mã để kiểm tra và ném ngoại lệ vô giá trị khi tất cả các loại đều đáp ứng một số điều kiện. Điều này dẫn đến cách codegen tốt hơn, do đó tôi đã nêu ra một vấn đề với MSVC STL và được trình bày với mã này:
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&) {}
}
Yêu cầu là, điều này làm cho bất kỳ biến thể nào trở nên vô giá trị, và đọc tài liệu nên:
Đầu tiên, phá hủy giá trị hiện có (nếu có). Sau đó, trực tiếp khởi tạo giá trị được chứa như thể xây dựng một giá trị loại
T_I
với các đối sốstd::forward<Args>(args)....
Nếu một ngoại lệ được đưa ra,*this
có thể trở thành valuless_by_exception.
Điều tôi không hiểu: Tại sao nó được nêu là "có thể"? Có hợp pháp để ở trong trạng thái cũ nếu toàn bộ hoạt động ném? Bởi vì đây là những gì GCC làm:
// 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>>
{ };
Và sau đó, nó (có điều kiện) làm một cái gì đó như:
T tmp = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);
Do đó về cơ bản, nó tạo ra tạm thời và nếu điều đó thành công các bản sao / di chuyển nó vào vị trí thực.
IMO đây là vi phạm "Đầu tiên, phá hủy giá trị hiện có" như tài liệu đã nêu. Khi tôi đọc tiêu chuẩn, sau đó sau khi v.emplace(...)
giá trị hiện tại trong biến thể luôn bị hủy và loại mới là loại được đặt hoặc không có giá trị.
Tôi nhận được rằng điều kiện is_trivially_copyable
loại trừ tất cả các loại có một hàm hủy có thể quan sát được. Vì vậy, điều này cũng có thể là: "biến thể as-if được khởi tạo lại với giá trị cũ" hoặc như vậy. Nhưng trạng thái của biến thể là một hiệu ứng có thể quan sát được. Vì vậy, tiêu chuẩn thực sự cho phép, điều emplace
đó không thay đổi giá trị hiện tại?
Chỉnh sửa để đáp lại một trích dẫn tiêu chuẩn:
Sau đó, khởi tạo giá trị được chứa như thể trực tiếp - không liệt kê - khởi tạo giá trị của loại TI với các đối số
std::forward<Args>(args)...
.
Có T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
thực sự được tính là một thực hiện hợp lệ của ở trên? Đây có phải là những gì có nghĩa là "như thể"?
might/may
từ ngữ vì tiêu chuẩn không nêu rõ sự thay thế là gì.