昨日これに遭遇しました。MSVC12 (VS2013, 120)およびMSVC14 (VS2015, 140)で失敗する明確で単純な例を挙げようとします。x64 ではすべてが暗黙的に /arch:SSE+ です。
説明のために、定義されたマクロ _MM_TRANSPOSE4_PS を使用して、この問題を単純な行列転置の例に単純化します。これは、L/H 8 バイト ブロックを移動するのではなく、シャッフルの観点から実装されています。
float4x4 Transpose(const float4x4& m) {
matrix4x4 n = LoadMatrix(m);
_MM_TRANSPOSE4_PS(n.row[0], n.row[1], n.row[2], n.row[3]);
return StoreMatrix(n);
}
これmatrix4x4
は、4 つのメンバーを含む単なる POD 構造体__m128
であり、多少暗黙的ではありますが、すべてが 16 バイト境界に整然と配置されています。
__declspec(align(16)) struct matrix4x4 {
__m128 row[4];
};
これはすべて、/O1、/O2、および /Ox で失敗します。
// Doesn't work.
float4x4 resultsPlx = Transpose( GiveMeATemporary() );
// Changing Transpose to take float4x4, or copy a temporary
float4x4 Transpose(float4x4 m) { ... }
// Trying again, doesn't work.
float4x4 resultsPlx = Transpose( GiveMeATemporary() );
不思議なことに、これは機能します:
// A constant reference to an rvalue, a temporary
const float4x4& temporary = GiveMeATemporary();
float4x4 resultsPlx = Transpose(temporary);
ポインターベースの転送についても同じことが言えます。これは、基になるメカニズムが同じであるため論理的です。C++11 仕様の関連部分は §12.2/5 です。
2 番目のコンテキストは、参照がテンポラリにバインドされる場合です。参照がバインドされている一時オブジェクト、または一時オブジェクトがバインドされているサブオブジェクトへの完全なオブジェクトである一時オブジェクトは、以下に指定されている場合を除き、参照の存続期間中存続します。コンストラクターの ctor-initializer (§12.6.2 [class.base.init]) 内の参照メンバーへの一時的なバインドは、コンストラクターが終了するまで持続します。関数呼び出し (§5.2.2 [expr.call]) の参照パラメーターへの一時的なバインドは、呼び出しを含む完全な式が完了するまで持続します。
これは、呼び出し環境が範囲外になるまで存続する必要があることを意味します。これは、関数が戻った後です。それで、何が得られますか?他のすべての場合、次の例外を除いて、変数は「最適化されずに」取得されます。
Access violation reading location 0xFFFFFFFFFFFFFFFF
解決策は明らかですが、ユーザーが他のライブラリのようにポインターベースの転送で一時ファイルを直接渡さないようにします。ビューを詰まらせずに、実際にはもう少しエレガントにすることを望んでいました.