# AlwaysFalse 常にFalseをを返すという謎の型。なんのために使われるんだろう? ## そもそもAlwaysFalseは何? [AlwaysFalse](https://source.chromium.org/chromium/chromium/src/+/main:base/types/always_false.h)はArgsの型をテンプレート引数に受け取ってそのままfalseが代入されている`kValue`を返すだけのbool値。 ```cpp= namespace internal { template <typename... Args> struct AlwaysFalseHelper { static constexpr bool kValue = false; }; } // namespace internal template <typename... Args> inline constexpr bool AlwaysFalse = internal::AlwaysFalseHelper<Args...>::kValue; ``` なら普通にfalseと書くのと違いなさそうに見える。 ## AlwaysFalseの用途 [static_assert](https://en.cppreference.com/w/c/language/_Static_assert)はコンパイル時に評価されるもので、例えば ```cpp= static_assert(sizeof(Layer) == 272, "Do not edit the size"); ``` みたいな感じにできる。 AlwaysFalseはこのstatic_assertの条件としてfalseをいれたいときに使う。 ではこのstatic_assertを常に落としたいのはどういうケースか? 以下の例を見てみる。 ```cpp= [[nodiscard]] BASE_EXPORT std::u16string UTF8ToUTF16(StringPiece utf8); template <size_t N> [[noreturn]] std::u16string UTF8ToUTF16(const char (&str)[N]) { static_assert(AlwaysFalse<decltype(N)>, "Error: Use u\"...\" to create a std::u16string literal."); } ``` これはUTF8ToUTF16という名前の通りUTF8の文字列をUTF16に変換するために使われるが、オーバーロードすることで引数として地の文字列を渡すような実装があるとコンパイルにstatic_assertで落ちるようになる。 2つ目の宣言がないと、`UTF8ToUTF16("hoge")`は"hoge"という文字列がStringPieceに暗黙に型変換され1つ目の宣言が普通に使われるが、2つ目の宣言があることでより強く型マッチするオーバーロードが選択されstatic_assertを呼んでくれるようになる。 ではなぜこれで`AlwaysFalse`ではなく普通に`false`を使ってはだめか? `static_assert(false, ...)`と書くとテンプレートがインスタンス化されない場合でも常にコンパイルに失敗してしまう。 しかし、AlwaysFalseのようにテンプレート引数に依存するような形だと、実際に該当のテンプレートがインスタンス化されるときに初めて評価されることになる。すなわちこの例だと`UTF8ToUTF16(const char (&str)[N])`の使い方をするコードが含まれているときにのみ評価されstatic_assertを通って常に落ちてくれるということ。 他にもテンプレートの特殊化で使っちゃいけないやつとかで使うこともできる。 つまりAlwaysFalseはなんらかのテンプレート引数とセットで使われる。 ## C++23 AlwaysFalseはC++23では必要なくなる。 `static_assert(false, ....)`がインスタンス化されていないテンプレートで使用されていてもコンパイルが通るようになるらしい。 [proposal](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html)によると以下のような書き方ができるようになるとのこと。 ```cpp= // if分岐の中 template <class T> void f(T t) { if constexpr (sizeof(T) == 4) { use(t); } else { static_assert(false, "must be size 4"); } } void g(char c) { f(c); // error: must be size 4 } // テンプレート特殊化の中 template <class T> struct should_be_specialized { static_assert(false, "this isn't the specialization you're looking for"); }; ```