最終更新日時:
が更新

履歴 編集

if constexpr 文(C++17)

概要

if constexpr文とは、コンパイル時に条件分岐する文である。

if constepxr ( condition )
  statement
else
  statement

if constexpr文の導入によってC++のif系の条件分岐文は3種類になった。

  • プリプロセス時if: #if
  • コンパイル時if: if constexpr
  • 実行時if: if

conditionはコンパイル時にコンテキスト依存のbool型への変換が起こる式である。例えばconstexpr指定されたexplicit operator bool()は評価できる。

else文にはconstexpr書かない。ifの後にconstexprを書く以外では実行時if文と構文に差はない。

プリプロセス時if文と異なり、if constexpr文は条件付きコンパイルをすることはできない。例えば次の例は違法である。

struct X {
  if constexpr (cond) {
      void f();
      using int32 = int;
  }
  else {
      void g();
  }
};

if constexpr文はスコープを作るので、例えばVisual C++の独自拡張機能である__if_existsは以下のような書き方が可能であるが、if constexpr文でこれを再現することはできない。

struct A {
    static float get() { return 1.2f;  }
};
int main() {
  auto a = __if_exists(A::get) {
    A::get();
  }
  __if_not_exists(A::get) {
    "not found";
  }
}

同様にD言語のstatic ifとは違いスコープを作るので、D言語で合法な次のようなことはif constexpr文で再現できない。

const int i = 3;
class C {
  const int k = 5;

  static if (i == 3) // D言語ではok
    int x;
  else
    long x;
}

if constexpr文で、実行されない方のstatementdiscarded statement(破棄文)となり、文の実体化を防ぐ。言い換えると、Two Phase Name Look-upにおけるdependent name(依存名)は、discarded statementの場合検証されない。また文が実体化されないのだから通常のif文と同じくもちろん実行時に実行もされない。つまり次の例は意図と異なる挙動を示す。

#include <type_traits>

template<typename T>
void f()
{
  if constexpr (std::is_same_v<T, int>)
  {
    // Tがintのときのみ発動してほしい
    // 実際は常に発動する
    static_assert(false) ;
  }
}

なぜならばdiscarded statementはテンプレートの実体化を防ぐ(依存名の検証をしない)だけで、被依存名は検証されるからである。この例のstatic_assertに渡す条件式はテンプレートパラメータに依存していないので、テンプレートの宣言時に検証され、エラーとなる。言い換えればstatic_assertに渡す条件式が依存名ならばテンプレートの宣言時に検証されず、テンプレート実体化まで評価を遅らせる事ができる。

#include <type_traits>

template <typename T>
constexpr bool false_v = false;
template<typename T>
void f()
{
  if constexpr (std::is_same_v<T, int>)
  {
    // Tがintのときのみ発動する
    static_assert(false_v<T>);
  }
}

if constexpr文の条件式内は実体化が起きる。したがって実体化するとコンパイルエラーになるものは書いてはいけない。

#include <type_traits>
#include <iostream>

struct Hoge {
    using type = int;
};

template<typename T>
void f()
{
  if constexpr (std::is_same_v<T::type, int> || std::is_same_v<T::value_type, int>) {
    std::cout << "is int" << std::endl;
  }
}
int main()
{
  f<Hoge>();//error: Hoge::value_typeは存在しないのでif constexpr文の条件式がコンパイルエラーになる
}

参照