質問: こんにちは!またよろしくお願いします! ペコリ(o_ _)o)) 初めに、次のコードはコンパイルエラーになります。case: 1 wandbox ``` #include struct S { constexpr auto operator()() const -> decltype(false) { return false; } }; template constexpr T get() { if constexpr (std::is_same_v) return 0; else static_assert(S{}(), "Lambda expression is evaluated."); } int main() { get(); } ``` つぎに、これはコンパイルが通ります。case: 2 wandbox ``` #include template constexpr T get() { struct S { constexpr auto operator()() const -> decltype(false) { return false; } }; if constexpr (std::is_same_v) return 0; else static_assert(S{}(), "Lambda expression is evaluated."); } int main() { get(); } ``` 質問1: case: 2 の S{}() は、get()が定義される際になぜ評価されないのでしょうか。local struct S はテンプレートではないので、インスタンス化を抑制するconstexpr ifは関係なく、case: 1 と同様に評価されるのではないでしょうか...? それとも、local struct S はテンプレート引数Tに依存しているのでしょうか... ~~質問2: constexpr ifとtwo-phase name lookupは関係があったのでしょうか。ずっとそう思っていたのですが、constexpr ifは"テンプレート引数に依存しているものの実体化を遅延する"と考えると、名前の検索のタイミングは関係ないような気がします...。 (追記) 質問2に関して、インスタンス化されていないテンプレートはそもそも使えないので、テンプレート定義の中に依存名があってもなくても同じと考えられないでしょうか。~~ (追記の追記) すみません...質問2はなかったことにしてください...
回答: こんにちは。 > > 質問1: case: 2 の S{}() は、get()が定義される際になぜ評価されないのでしょうか。 > > > case: 2のSは省略しないで記述すると、get()::Sになりますので形式的にはTに依存していることになるようです。 > > 質問2: constexpr ifとtwo-phase name lookupは関係があったのでしょうか。 > > > テンプレート内で参照されるテンプレート引数に依存した名前の解決はtwo-phase name lookupされると理解しています。(two-phase name lookupについては良く知らないですが、アセンブラの1パス、2パスと同様な問題と理解しています。余談ですがC/C++も2パスを許せば本当に楽になるのですが...) 通常は1phase目に名前解決されます。しかし、テンプレート引数に与える「型」についてテンプレートを定義する前に全て定義されていることを必須とするのはナンセンスと思います。テンプレートを定義する前に明示的実体化的に引数を全て指定する必要がありますから。そこで、1phase目ではテンプレート引数に依存した名前は未解決であることを許しているのだと思います。 そして、2phase目はテンプレートの実体化時に行われる筈です。その際constexpr ifは条件が成立しない方の2phase目以降を実行しないということではないでしょうか? コンパイル時に決定できるものですからそれでOKと思います。通常のif文は実行時に分岐する「建前」なので通常のif文にするとアサートします。 ああ、そうすると前回書いた「実体化を遅延する効果」とう表現は不適切でした。通常のif文でもテンプレート引数に依存したものは当然「実体化は遅延」されます。通常のif文では実体化が抑制されないので普通にアサートするわけです。 「実体化を抑制する効果」と書くべきでした。(リンク先もそう書いてありました。申し訳ないです。)