2016-10-28 5 views
7

私はstd::optional実装では、このコードで出くわした:このsizeof式はどのように評価されますか?それはなぜそのように呼ばれていますか?

template <class T, class U> 
struct is_assignable 
{ 
    template <class X, class Y> 
    constexpr static bool has_assign(...) { return false; } 

    template <class X, class Y, size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) > 
    // the comma operator is necessary for the cases where operator= returns void 
    constexpr static bool has_assign(bool) { return true; } 

    constexpr static bool value = has_assign<T, U>(true); 
}; 

私はそれがどのように動作するかまたはそれを評価する方法を理解カント一部がsize_t S = sizeof((std::declval<X>() = std::declval<Y>(), true))である私は割り当て操作が失敗した場合、それがバックに落ちることを知っていますfalseを返すhas_assignの最初の定義ですが、なぜ, true)という部分があるのか​​わかりません。

私は代入演算子でvoidを返し、sizeof, true部分を削除しても、同じ結果が得られます。

+2

http://en.cppreference.com/w/cpp/language/sfinae。与えられた 'X'と' Y'に対して 'sizeof'の式が有効であれば、' has_assign'の2つのオーバーロードがあり、もう一つは 'has_assign (true)'の方が良い一致として選択されます。式が無意味な場合、その過負荷は破棄され、最初のものが選択されます。 –

+1

一部のコンパイラでは、 'sizeof(void)== 1'を拡張子として定義しています。より多くの警告を有効にします。 – Quentin

+2

'class S = decltype(std :: declat ()= std :: decl ())'と書く方がより直接的でしょうか。とにかく 'S 'が何であっても、その式が有効であることは気にしません。 – Barry

答えて

7

sizeof()を適用するためには、次のものが必要完全な型。しかし、完全な型を返すことは、したがって、assignabilityの要件ではない:割り当ては、これら二つのタイプのために有効であるかどう

sizeof((std::declval<X>() = std::declval<Y>(), true)) 
     ~~~~~~~~~~~~~~~~~~ expr ~~~~~~~~~~~~~~~~~~~~~ 

、我々はexprのタイプはbooltrueため)であるsizeof(expr)を持っています。したがって、割り当てが有効であれば、実際の値はsizeになります。それ以外の場合は、置換に失敗します。


しかし、これは不必要に暗黙のうちにこのコードを書く方法です。

struct Evil { 
    template <class T> Evil operator=(T&&); // assignable from anything 
    void operator,(bool);     // mwahahaha 
}; 

、今お使いのsizeof()まだ動作しません。私のようなタイプを記述する可能性があるため、また、それも正解ではありません。代わりに

は、単に好む:結果のタイプがあるか、特別な場合を扱うためにかについては全く気にする必要なし - - 置換の故障かのどちらか

class = decltype(std::declval<X>() = std::declval<Y>()) 

これは、同じ結果を達成します。

+0

元のコードは一部のコンパイラで動作しますか? 生成されるコードには、1つの利点があります。 'sizeof(true)'は常に同じサイズになります(互換性のあるオプションでコンパイルされたすべてのファイルを想定しています)。 – Phil1970

+0

@ Phil1970これはすべてで完了しています。コンパイル時。これらの関数は、オブジェクトファイルには表示されません。 – Barry

+0

ちょっとメモしておきますが、is_assignable(標準のものでさえ)が偽陽性を与える場合があります。しかし、私は、この問題について新たな質問を掲げることができると思う。 [gist](https://gist.github.com/aindigo/69bf0ca558a127321af70dbb82314f5c) – alter

7

原則として、式std::declval<X>() = std::declval<Y>()のタイプ(すなわち、operator =の戻りタイプ)は、不完全タイプまたはvoidを含む任意である可能性があります。そのような場合、SFINAEは式が有効であるために蹴られません。ただし、sizeofを不完全な型に適用するとエラーが発生します。 (一部のコンパイラでは、拡張子としてsizeof(void) == 1が定義されていますが、普遍的に依存することはできません)。

SFINAE発現が割り当ての種類を破棄することによって、これを修正した後に(それが何であれ), trueを加え、(完全に有効である)代わりtruesizeofを適用します。

コメントでBarryによって示されるように、より直接的なアプローチは、このように、decltypeではなく、sizeofに割り当てのタイプを使用することです:

template <class X, class Y, class S = decltype(std::declval<X>() = std::declval<Y>()) > 
constexpr static bool has_assign(bool) { return true; } 
関連する問題