私は最近、std::visit
コンパイラ間の最適化の素晴らしい比較につながるRedditディスカッションをフォローしました。私は次のことに気づきました:https : //godbolt.org/z/D2Q5ED
GCC9とClang9の両方(同じstdlibを共有していると思います)は、すべての型がいくつかの条件を満たす場合に、値のない例外をチェックしてスローするためのコードを生成しません。これはより優れたcodegenにつながるため、MSVC STLで問題を提起し、次のコードが提示されました。
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&) {}
}
主張によると、これによりすべてのバリアントが無価値になり、ドキュメントを読むと次のようになります。
まず、現在含まれている値(存在する場合)を破棄します。次に
T_I
、引数を使用してtypeの値を作成する場合と同様に、含まれている値を直接初期化します。std::forward<Args>(args)....
例外がスローされた場合、*this
valueless_by_exceptionになることがあります。
わからないこと:なぜ「かもしれない」と記載されているのですか?操作全体がスローされた場合、古い状態を維持することは合法ですか?これは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>>
{ };
そして後でそれは(条件付きで)次のようなことをします:
T tmp = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);
したがって、基本的には一時ファイルを作成し、それが成功すると、それを実際の場所にコピー/移動します。
IMOこれは、ドキュメントで述べられている「最初に、現在含まれている値を破棄する」の違反です。私が標準を読んだときv.emplace(...)
、バリアントの現在の値は常に破棄され、新しい型はセット型または値なしのいずれかになります。
この条件is_trivially_copyable
では、観測可能なデストラクタを持つすべての型が除外されています。したがって、これは、「as-ifバリアントが古い値で再初期化される」などと考えられます。しかし、バリアントの状態は観察可能な影響です。それで標準は実際に許可しemplace
ますか、それは現在の値を変更しませんか?
標準的な見積もりに応じて編集します。
次に、含まれている値を、引数でTI型の値を直接非リスト初期化するかのように初期化します
std::forward<Args>(args)...
。
T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
本当に上記の有効な実装としてカウント?これは「まるで」の意味ですか?
might/may
標準が代替案が何であるかを述べていないので、私はこの文言に非常に混乱しています。