8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

静的リフレクションでニコニコしよう^^

8
Last updated at Posted at 2025-12-20

はじめに

C++26についに静的リフレクションがくるらしい
最終的にどうなったのかあまり追えてなかったので、とりあえず調べてみる

なんかニコニコしてて元気になれそうな感じなのは把握している^^

言語機能といくつかのメタ関数を見ていきます

言語機能

constexpr auto r = ^^int;
typename[:r:] x = 42;       // Same as: int x = 42;
  • リフレクション演算子 ^^ (ニコニコ演算子ではない^^)
    • std::meta::info 型のリフレクション値を返す
    • リフレクション演算子が使用できるのは以下のような物がある
      • 名前空間 / 名前空間エイリアス
        • (^^::はグローバル名前空間へのリフレクション値)
      • 型 / 型エイリアス
      • 変数、staticデータメンバ、構造化束縛
      • 関数 / メンバ関数
      • 非staticデータメンバ
      • template
      • enum値
  • スプライサー構文 [:...:]
    • リフレクション値から戻すほうの構文

constevalブロック

consteval {
  // statement
}

これは以下と同等です

static_assert(
  (
    []() -> void consteval {
       // statement
    }(),
    true
  )
);

constevalブロックとdefine_aggregateを組み合わせて
リフレクション値からクラスのデータメンバを定義できます。

#include <meta>

struct Hoge; // 宣言のみ
consteval
{
    // Hoge型の定義
    std::meta::define_aggregate(^^Hoge, {
         // int piyo;
         data_member_spec(^^int, {.name="piyo"})
    });
}
// 以下のような定義ができた
// struct Hoge
// {
//     int piyo;
// }
Hoge hoge{.piyo = 1};

template for

リフレクションを扱う際に一緒に覚えておくのが良いC++26の新構文

template <typename... Ts>
void print_all(Ts... elems) {
  template for (auto elem : {elems...}) {
    std::println("{}", elem);
  }
}

上記が以下のように展開されます

template <typename... Ts>
void print_all(Ts... elems) {
{
    {
      auto elem = elems...[0];
      std::println("{}", elem);
    }

    {
      auto elem = elems...[1];
      std::println("{}", elem);
    }

    {
      auto elem = elems...[2];
      std::println("{}", elem);
    }
  }
}

ついでに余談ですが、C++26でパラメータパックへのインデックスアクセスができるようになりました。

template <typename... Ts>
void print_first(Ts... elems)
{
    std::println("{}", elems...[0]);
}

tupleの展開

auto tup = std::make_tuple(0, 'a');
template for (const auto& elem : tup) {
    std::println("{}", elem);
}

リフレクション関連 メタ関数

identifier_of

識別子の名前を取得できる
has_identifierで識別子か判定できる

int a;
constexpr auto r = ^^a;
static_assert(std::meta::has_identifier(r));
std::println("{}", std::meta::identifier_of(r)); // a

display_string_of

こちらは、引数のリフレクション値が識別子でなくても良いが、実装によるいい感じの名前を返してくれる

std::println("{}", std::meta::display_string_of(^^int)); // int

source_location_of

リフレクション値から実装によるいい感じのsource_locationを取得する

#include <print>
#include <meta>

int main()
{
    int a; // 6行名で宣言
    constexpr auto r = ^^a;
    constexpr auto location = std::meta::source_location_of(r);
    std::println("{}", location.line()); // 6
}

type_of

型エンティティのリフレクション値から、型のリフレクション値を取得できます

int a;
static_assert(std::meta::type_of(^^a) == ^^int);

parent_of

クラスや関数のスコープに含むものや、名前空間を指定し、どこに属するかを取得できます

namespace My
{
    struct Hoge
    {
        int piyo;
    };
}

int main()
{
    int a;
    static_assert(std::meta::parent_of(^^a) == ^^main);
    static_assert(std::meta::parent_of(^^My::Hoge::piyo) == ^^My::Hoge);
    static_assert(std::meta::parent_of(^^My::Hoge) == ^^My);
}

dealias

エイリアスの元のリフレクション値を取得します

using MyInt = int;
static_assert(^^MyInt != ^^int);
static_assert(std::meta::dealias(^^MyInt) == ^^int);

object_of

静的記憶領域期間をもつオブジェクトのリフレクション値を取得します
すでにオブジェクトのリフレクション値であるものを渡したら、その値がそのまま返る。

static int x;
int &y = x;
static_assert(^^x != ^^y);
static_assert(std::meta::object_of(^^x) == std::meta::object_of(^^y));

constant_of

定数値のリフレクション値を取得します。

  • 列挙子の場合、その値のリフレクション値
  • 定数式で使用できるオブジェクトの場合
    • スカラーならその値のリフレクション値。
    • そうでないならオブジェクトのリフレクション値
constexpr int a = 0;
constexpr int b = 0;
static_assert(^^a != ^^b);
static_assert(std::meta::constant_of(^^a) == std::meta::constant_of(^^b));

template_of

特殊化された型のリフレクション値からテンプレートのリフレクション値を取得します

static_assert(std::meta::template_of(^^std::vector<int>) == ^^std::vector);

template_arguments_of

特殊化された型のリフレクション値からテンプレート引数のリフレクション値のvectorを取得します

static_assert(std::meta::template_arguments_of(^^std::vector<int>)[0] == ^^int);

members_of

classや名前空間に含むリフレクション値のvectorを宣言順に取得します


namespace Hoge
{
    int a;
    void f();
}

constexpr auto ctx = std::meta::access_context::current();
static_assert(std::meta::members_of(^^Hoge, ctx)[0] == ^^Hoge::a);
static_assert(std::meta::members_of(^^Hoge, ctx)[1] == ^^Hoge::f);

static_data_members_of

classのstaticデータメンバのリフレクション値のvectorを宣言順に取得します

nonstatic_data_members_of

classの非staticデータメンバのリフレクション値のvectorを宣言順に取得します

struct Hoge
{
    int m_a;
    int m_b;
    static int s_a;
    static int s_b;

    using A = int;
};

constexpr auto ctx = std::meta::access_context::current();
static_assert(std::meta::static_data_members_of(^^Hoge, ctx)[0] == ^^Hoge::s_a);
static_assert(std::meta::nonstatic_data_members_of(^^Hoge, ctx)[0] == ^^Hoge::m_a);
static_assert(std::meta::members_of(^^Hoge, ctx)[4] == ^^Hoge::A);

bases_of

classの直接基底クラス関係のリフレクション値を宣言順に取得します

struct A{};
struct B : A{};

constexpr auto ctx = std::meta::access_context::current();
constexpr auto r = std::meta::bases_of(^^B, ctx)[0];
static_assert(std::meta::parent_of(r) == ^^B);
static_assert(std::meta::type_of(r) == ^^A);

enumerators_of

列挙子のリフレクション値のvectorを取得

enum class Enum
{
    Hoge,
    Piyo,
    Foo,
};


static_assert(std::meta::enumerators_of(^^Enum)[0] == ^^Enum::Hoge);
static_assert(std::meta::enumerators_of(^^Enum)[1] == ^^Enum::Piyo);
static_assert(std::meta::enumerators_of(^^Enum)[2] == ^^Enum::Foo);

substitute

templateのリフレクション値とテンプレート引数のリフレクション値を組み合わせる

constexpr auto r = substitute(^^std::vector, {^^int});
static_assert(r == ^^std::vector<int>);

templateがconceptの場合は、boolの定数リフレクション値になる

constexpr auto r = substitute(^^std::integral, {^^int});
static_assert([:r:]);

constexpr auto r2 = substitute(^^std::integral, {^^float});
static_assert(![:r2:]);

reflect_constant

評価結果から定数値のリフレクション値を生成

constexpr int x = 5;
static_assert(std::meta::reflect_constant(5) == std::meta::constant_of(^^x));

reflect_object

評価結果からオブジェクトのリフレクション値を生成

static int x = 5;
static_assert(std::meta::reflect_object(x) == std::meta::object_of(^^x));

reflect_function

評価結果から関数のリフレクション値を生成

void foo();

consteval bool is_global_func(void (*f)())
{
    auto rf = std::meta::reflect_function(*f);
    return parent_of(rf) == ^^::;
}

static_assert(is_global_func(&foo));

extract<T>

リフレクション値をT型として取り出す

constexpr int x = 1;
constexpr auto r = ^^x;
static_assert(extract<int>(r) == 1);

レイアウト系

offset_of

非静的データメンバや、基底クラスサブオブジェクトのリフレクション値から先頭からのオフセットを取得

size_of

型、オブジェクト、変数、非静的データメンバ、基本クラスサブオブジェクトのリフレクション値からバイトサイズを返す

alignment_of

型、非静的データメンバ、基本クラスサブオブジェクトのリフレクション値からアライメントを返す

bit_size_of

非静的データメンバや、基底クラスサブオブジェクトのリフレクション値からビットサイズを返す

struct Hoge
{
    std::int32_t piyo;
    std::int32_t foo;
};

static_assert(std::meta::offset_of(^^Hoge::piyo) == std::meta::member_offset{0, 0});
static_assert(std::meta::offset_of(^^Hoge::foo) == std::meta::member_offset{4, 0});

static_assert(std::meta::size_of(^^Hoge::piyo) == 4);
static_assert(std::meta::alignment_of(^^Hoge::piyo) == 4);
static_assert(std::meta::bit_size_of(^^Hoge::piyo) == 32);

その他判定系

いっぱいあるので一部紹介

// 名前空間か
namespace My{}
static_assert(std::meta::is_namespace(^^My));  

// 名前空間エイリアスか
namespace My2 = My;
static_assert(std::meta::is_namespace_alias(^^My2));  

// 関数か
void func();
static_assert(std::meta::is_function(^^func));  

// 変数か
constexpr int a = 0;
static_assert(std::meta::is_variable(^^a));  

// 型か
struct Hoge;
static_assert(std::meta::is_type(^^Hoge));  
// 完全型か
static_assert(!std::meta::is_complete_type(^^Hoge));  

// 型エイリアスか
using MyInt = int;
static_assert(std::meta::is_type_alias(^^MyInt));  

// enumか
enum class Enum{};
static_assert(std::meta::is_enumerable_type(^^Enum));  

// templateか
static_assert(std::meta::is_template(^^std::vector));

// conceptか
static_assert(std::meta::is_concept(^^std::integral));

// 値か
static_assert(std::meta::is_value(std::meta::constant_of(^^a)));  

// オブジェクトか
static_assert(std::meta::is_object(std::meta::object_of(^^a)));  

修飾子系

アクセス指定子

struct Hoge
{
public:
    int a;
protected:
    int b;
private:    
    int c;
    
  // publicか
  static_assert(std::meta::is_public(^^Hoge::a));
  // protectedか
  static_assert(std::meta::is_protected(^^Hoge::b));
  // privateか
  static_assert(std::meta::is_private(^^Hoge::c));    
};

CV

const volatile int a = 0;
// constか
static_assert(std::meta::is_const(^^a));
// volatileか
static_assert(std::meta::is_volatile(^^a));

などなど…

まとめ

  • C++26についに静的リフレクションがくる
  • ソースコードがニコニコ ^^ しているが、これはリフレクション演算子
  • スプライサー構文 [:...:]でリフレクション値から戻せる
  • constevalブロックとリフレクションを使ってメンバー変数の定義ができる
    • define_aggregate
    • data_member_spec
  • template forありがてえ
  • 豊富なメタ関数群でいろいろできそう
  • 静的リフレクション早く使いたい
  • 静的リフレクションでニコニコ ^^しよう

参考

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?