Son zamanlarda std::visit
, derleyiciler arasında optimizasyonun güzel bir karşılaştırmasına yol açan bir Reddit tartışmasını izledim . Aşağıdakileri fark ettim: https://godbolt.org/z/D2Q5ED
Hem GCC9 hem de Clang9 (sanırım aynı stdlib'i paylaşıyorlar), tüm türler bazı koşulları karşıladığında değersiz bir istisnayı kontrol etmek ve atmak için kod oluşturmazlar. Bu daha iyi kodgen yol açar, bu nedenle MSVC STL ile ilgili bir sorun gündeme getirdi ve bu kod ile sunuldu:
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&) {}
}
İddia şuydu, bunun herhangi bir varyasyonu değersiz hale getirmesi ve dokümanı okuması gerekir:
İlk olarak, mevcut değeri (varsa) yok eder. Ardından, içerilen değeri
T_I
bağımsız değişkenlerlestd::forward<Args>(args)....
bir tür değeri oluşturuyormuş gibi doğrudan başlatır. Bir istisna atılırsa,*this
valueless_by_exception haline gelebilir.
Ne anlamıyorum: Neden "mayıs" olarak ifade edilir? Tüm operasyon atarsa eski halde kalmak yasal mıdır? Çünkü GCC'nin yaptığı budur:
// 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>>
{ };
Ve daha sonra (şartlı olarak) şöyle bir şey yapar:
T tmp = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);
Bu temelde bir geçici oluşturur ve bu başarılı olursa kopyalar / gerçek yere taşır.
IMO bu, docu tarafından belirtildiği gibi "İlk olarak, şu anda mevcut olan değeri yok eder" ihlalidir. Standardı okuduğumdan sonra v.emplace(...)
, varyanttaki mevcut değer her zaman yok edilir ve yeni tip ya ayarlı tip ya da değersizdir.
is_trivially_copyable
Durumun gözlenebilir bir yıkıcıya sahip tüm türleri hariç tuttuğunu anlıyorum . Dolayısıyla bu şu şekilde de olabilir: "as-if varyantı eski değerle yeniden başlatıldı" ya da öylesine. Ancak varyantın durumu gözlemlenebilir bir etkidir. Standart gerçekten buna izin veriyor mu, bu emplace
mevcut değeri değiştirmiyor mu?
Standart bir teklife yanıt olarak düzenleme:
Ardından, içerilen değeri argümanlarla TI türünde bir değeri doğrudan liste dışı olarak başlatır gibi başlatır
std::forward<Args>(args)...
.
T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
Gerçekten yukarıdakilerin geçerli bir uygulaması sayılır mı ? "Sanki" ile kastedilen bu mudur?
might/may
Standart alternatifin ne olduğunu belirtmediği için ifadelerle çok kafam karıştı .