最終更新日時(UTC):
が更新

履歴 編集

定数式でのdynamic_cast、多態的なtypeidを許可 [P1327R1](C++20)

このページはC++20に採用された言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため関連項目を参照してください。

概要

C++20より、constexpr関数内(定数式)でdynamic_castおよび多態的な型に対するtypeidを使用することができるようになる。

ただし、定数式でのdynamic_cast/typeidの実行が例外を投げる場合は定数式で実行可能ではない。そのコンテキストが定数式で実行されなければならない所(constexpr変数の初期化式など)ではない場合、その実行は実行時まで延期される。

struct base {
  virtual int f() const = 0;
};

struct derived1 : public base {
  constexpr int f() const override {
    return 10;
  }
};

struct derived2 : public base {
  constexpr int f() const override {
    return 20;
  }
};

struct base2 {
  virtual int g() const = 0;
};

struct derived3 : public base, public base2 {
  constexpr int f() const override {
    return 30;
  }

  constexpr int g() const override {
    return 30;
  }
};

constexpr auto& get_typeinfo(const base* p) {
  // コンパイル時に動的型のtype_infoを取得する
  return typeid(*p);
}

constexpr int side_cast(const base* p) {
  // コンパイル時サイドキャスト
  const base2* b2 = dynamic_cast<const base2*>(p);
  return b2->g();
}

int main() {
  constexpr derived1 d1{};
  constexpr derived2 d2{};

  // typeidの例
  constexpr auto& tid1 = get_typeinfo(&d1);
  constexpr auto& tid2 = get_typeinfo(&d2);

  std::cout << std::boolalpha;
  std::cout << (tid1 == tid2) << std::endl;

  // dynamic_castの例
  constexpr derived3 d3{};
  constexpr int n = side_cast(&d3);

  static_assert(n == 30);
  std::cout << n << std::endl;
}

出力

false
30

この機能が必要になった背景・経緯

std::error_categoryに新しい仮想関数やメンバを追加する提案が提出されていたが、ABIを破損する(クラスレイアウトが変更されてしまう)ことから採択されなかった。

代替案として、std::error_categoryを派生させた別の型を導入して、その型に対して変更を加えるという方法が検討された。その場合、std::error_codeでは、dynamic_cast/typeidを使用して新旧error_categoryのどちらを利用しているのかをチェックした上で処理を分ける事になる。

しかしその方法は、std::error_codeのメンバ関数をconstexprにする提案と衝突してしまっていた。dynamic_castおよび多態的な型に対するtypeidは定数式で実行可能ではなかったためである。

この提案の採択以前に、C++20にて定数式での仮想関数呼び出しが許可され、コンパイラは定数式においてあるオブジェクトの動的型を追跡し続けるようになっていた。したがって、dynamic_castおよび多態的な型に対するtypeidを定数式で利用できない技術的な理由は取り除かれており、これらを定数式で実行できないという制限は緩和されることになった。

検討されたほかの選択肢

別の提案(P1328R0)にてtypeinfooperator==constexprにする事が提案されている。これは当初C++20に導入される予定であったが、実装可能性に関する懸念が提起されその調査に時間がかかったためC++20には間に合わなかった。

その後、実装可能性に関する懸念は払拭されたため、C++23に導入される予定である。

関連項目

参照