---
title: Reflection@C++26
tags: 
author: [dechimal](https://www.docswell.com/user/dechimal)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/D7Y4LYGPEM.jpg?width=480
description: Reflection@C++26 by dechimal
published: June 19, 26
canonical: https://www.docswell.com/s/dechimal/K7N4PD-2026-06-19-013514
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/D7Y4LYGPEM.jpg)

実⾏時に評価を
許さないReﬂection
@C++��
�/��


# Page. 2

![Page Image](https://bcdn.docswell.com/page/VENY4PGMJ8.jpg)

C++��の新機能: 静的リフレクション
• コンパイル時にC++の様々なエンティティの情報を取得する
機能
• 実⾏時には使⽤できない
• 限定的なクラス⽣成
�/��


# Page. 3

![Page Image](https://bcdn.docswell.com/page/Y79PQ3WWE3.jpg)

エンティティの情報を取得する
namespace ns {
struct type {};
void func();
int var = 0;
}
// 各エンティティの種類をチェックする
// ^^はリフレクションオブジェクト(以下、単にinfoとする)を
// 取得するための演算⼦
static_assert(meta::is_namespace(^^ns));
// 名前空間
static_assert(meta::is_type(^^ns::type));
// 型
static_assert(meta::is_variable(^^ns::var)); // 変数
// varはnsに属している
static_assert(meta::parent_of(^^ns::var) == ^^ns);
// 名前空間nsの最初のメンバーはtype
constexpr info first = meta::members_of(
^^ns, meta::access_context::unchecked())[0];
static_assert(first == ^^ns::type);
�/��


# Page. 4

![Page Image](https://bcdn.docswell.com/page/G78DGMNR7D.jpg)

アノテーション
• エンティティにユーザー定義の情報を添付する機能
• 添付した情報は他と同様にinfoから取得できる
struct [[=123]] type {};
static constexpr info type_anno0
= meta::annotations_of(^^type)[0];
static_assert(meta::extract&lt;int&gt;(type_anno0) == 123);
�/��


# Page. 5

![Page Image](https://bcdn.docswell.com/page/L7LMG3P2JR.jpg)

実⾏時には使⽤できない
int main() {
// info r1 = ^^int;
// エラー 実⾏時の変数にinfoを使った
constexpr info r2 = ^^int; // OK
// 以下のdisplay〜(r2)も実⾏時ではなくコンパイル時に評価されている
std::println(&quot;{}&quot;, meta::display_string_of(r2));
}
�/��


# Page. 6

![Page Image](https://bcdn.docswell.com/page/4EMYQD49EW.jpg)

限定的なクラス⽣成
• リフレクション関数を使ってクラスの中⾝を⽣成できる。
• 前⽅宣⾔のみのクラスや、実体化されていないクラステンプ
レート識別⼦に対してのみ使⽤可能。
◦ 既存クラスへのメンバー追加はできない。
• ⽣成できるのはデータメンバーのみ。
◦ メンバー関数を定義したり基底クラスを指定したりは
できない。
�/��


# Page. 7

![Page Image](https://bcdn.docswell.com/page/PER984W9J9.jpg)

限定的なクラス⽣成(普通のクラス)
struct point;
consteval {
meta::define_aggregate(^^point, {
meta::data_member_spec(^^int, { .name = &quot;x&quot; }),
meta::data_member_spec(^^int, { .name = &quot;y&quot; }),
});
}
int main() {
point p = { .x = 10, .y = 20 };
std::println(&quot;{}, {}&quot;, p.x, p.y);
}
�/��


# Page. 8

![Page Image](https://bcdn.docswell.com/page/P7XQ82P3EX.jpg)

限定的なクラス⽣成(テンプレート特殊化)
template&lt;typename T&gt;
struct t { int a; };
consteval {
meta::define_aggregate(^^t&lt;int&gt;, {
meta::data_member_spec(^^int, { .name = &quot;b&quot; }),
});
}
int main() {
t&lt;char&gt; tc = { .a = 10 };
std::println(&quot;{}&quot;, tc.a);
t&lt;int&gt; ti = { .b = 10 };
std::println(&quot;{}&quot;, ti.b);
}
�/��


# Page. 9

![Page Image](https://bcdn.docswell.com/page/37K9KMDN7D.jpg)

限定的なクラス⽣成
• リフレクション関数を使ってクラスの中⾝を⽣成できる。
• 前⽅宣⾔のみのクラスや、実体化されていないクラステンプ
レート識別⼦に対してのみ使⽤可能。
◦ 既存クラスへのメンバー追加はできない。
• ⽣成できるのはデータメンバーのみ。
◦ メンバー関数を定義したり基底クラスを指定したりは
できない。
�/��


# Page. 10

![Page Image](https://bcdn.docswell.com/page/LJ3WZYRZJ5.jpg)

せやろか？
��/��


# Page. 11

![Page Image](https://bcdn.docswell.com/page/8JDKR5Y4EG.jpg)

メンバー関数⽣成を
許していないReﬂection
を出し抜く@C++��
��/��


# Page. 12

![Page Image](https://bcdn.docswell.com/page/VEPKWG6V78.jpg)

関数オブジェクトデータメンバー
struct print_t {
static void operator()() {
std::println(&quot;{}&quot;, &quot;hello&quot;);
}
};
struct hoge;
consteval {
meta::define_aggregate(^^hoge, {
meta::data_member_spec(^^print_t, { .name = &quot;print&quot; })
});
}
int main() {
hoge x;
x.print();
}
なんかそれっぽいことはできる。
��/��


# Page. 13

![Page Image](https://bcdn.docswell.com/page/27VV863R7Q.jpg)

関数オブジェクトデータメンバー
にthisを認識させる
メンバー関数なら this で他のメンバーにもアクセスしたい。
template&lt;typename Self, std::size_t I&gt;
struct print_t {
void operator()() {
std::println(&quot;{}&quot;, self().msg);
}
Self const &amp; self() const {
return *(Self *)((char *)this - offset());
}
static consteval std::size_t offset() {
info mem = meta::nonstatic_data_members_of(
^^Self, meta::access_context::unchecked())[I];
return meta::offset_of(mem).bytes;
}
};
��/��


# Page. 14

![Page Image](https://bcdn.docswell.com/page/5JGL5N367L.jpg)

関数オブジェクトデータメンバー
にthisを認識させる
メンバー関数なら this で他のメンバーにもアクセスしたい。
struct hoge;
consteval {
meta::define_aggregate(^^hoge, {
meta::data_member_spec(^^std::string, { .name = &quot;msg&quot; }),
meta::data_member_spec(^^print_t&lt;hoge, 1&gt;, { .name = &quot;print&quot; }),
});
}
int main() {
hoge x { .msg = &quot;quack&quot; };
x.print();
}
��/��


# Page. 15

![Page Image](https://bcdn.docswell.com/page/47QYZQ52EP.jpg)

関数を外から指定できるようにする
template&lt;typename Self, auto F, std::size_t I&gt;
struct memfun_t {
template&lt;typename FnSelf, typename ...Args&gt;
decltype(auto) operator()(this FnSelf &amp;&amp; fn_self, Args &amp;&amp; ...args) {
return F(
std::forward_like&lt;FnSelf&gt;(fn_self.self()),
std::forward&lt;Args&gt;(args)...);
}
Self &amp; self() const {
return *(Self *)((char *)this - offset());
}
static consteval std::size_t offset() {
info m = meta::nonstatic_data_members_of(
^^Self, meta::access_context::unchecked())[I];
return meta::offset_of(m).bytes;
}
};
��/��


# Page. 16

![Page Image](https://bcdn.docswell.com/page/KE4W3N9PJ1.jpg)

関数を外から指定できるようにする
struct hoge;
consteval {
constexpr auto f = [] (auto &amp;&amp; self) {
std::println(&quot;{}&quot;, self.msg);
};
meta::define_aggregate(^^hoge, {
meta::data_member_spec(^^std::string, { .name = &quot;msg&quot; }),
meta::data_member_spec(^^memfun_t&lt;hoge, f, 1&gt;, { .name = &quot;print&quot; }),
});
}
int main() {
hoge x = { .msg = &quot;quack&quot; };
x.print();
}
��/��


# Page. 17

![Page Image](https://bcdn.docswell.com/page/L71Y15KXJG.jpg)

許せないReﬂection
の挙動
@C++��
��/��


# Page. 18

![Page Image](https://bcdn.docswell.com/page/G7WG85DKE2.jpg)

vectorを返してくる関数
• std::vector はコンパイル時に評価可能である。
• が、constexpr変数にはできない(※)のでよくあるケースで
使い勝⼿が悪い。
◦ ※細かい話を置いとくと「できない」と⾔ってよい。
◦ 使い勝⼿が悪いので std::define_static_array という関数
が提供されている。
◦ あとGCCの実装もなんか怪レい。
��/��


# Page. 19

![Page Image](https://bcdn.docswell.com/page/4JZL8NGNE3.jpg)

vectorを返してくる関数
メンバー⼀覧を出力する
template&lt;info E&gt;
void print_members() {
template for (constexpr info mem:
meta::members_of(E, meta::access_context::unchecked()))
{ std::println(&quot;{}&quot;, meta::display_string_of(mem)); }
}
エラー
• この形式の template for は meta::members_of の戻り値を constexpr
変数で受ける。
◦ 戻り値の型は std::vector&lt;info&gt;
• new で取ったメモリを抱えるオブジェクトは constexpr 変数に
保存できない。
��/��


# Page. 20

![Page Image](https://bcdn.docswell.com/page/YE6WP8Y9EV.jpg)

vectorを返してくる関数
メンバー⼀覧を出力する
template&lt;info E&gt;
void print_members() {
template for (constexpr info mem:
std::define_static_array(
meta::members_of(E, meta::access_context::unchecked())))
{ std::println(&quot;{}&quot;, meta::display_string_of(mem)); }
}
通る
• std::define_static_array 関数は範囲から静的な配列を作り出
し、その配列を指す std::span を返す。 new で確保したメモリ
は⼀切ない。
• std::span はポインタを持っているだけ。 new は⼀切ない。
��/��


# Page. 21

![Page Image](https://bcdn.docswell.com/page/GE5MK9GDE4.jpg)

vectorを返してくる関数
メンバー⼀覧を出力する
template&lt;info E&gt;
void print_members() {
constexpr auto mems = std::define_static_array(
meta::members_of(E, meta::access_context::unchecked()));
template for (constexpr info mem: mems)
{ std::println(&quot;{}&quot;, meta::display_string_of(mem)); }
}
なんかエラー
• GC⼦⽈く「反復する対象のmemsがローカル変数だと、同
じ引数でも評価の度にmemsのアドレスが変わるからよくな
い。memsをstaticにしなさい」。
• しかし規格を読んでもその旨を読み取れなかった。
��/��


# Page. 22

![Page Image](https://bcdn.docswell.com/page/9729W2DMJR.jpg)

vectorを返してくる関数
メンバー⼀覧を出力する
template&lt;info E&gt;
void print_members() {
constexpr auto mems = std::define_static_array(
meta::members_of(E, meta::access_context::unchecked()));
template for (constexpr info mem: auto(mems))
{ std::println(&quot;{}&quot;, meta::display_string_of(mem)); }
}
なんか通る
• ⼀時オブジェクトならアドレスはどうでもいいらしい
• 本当にこんな動作で合っているの⋯⋯？
��/��


# Page. 23

![Page Image](https://bcdn.docswell.com/page/DJY4LY2P7M.jpg)

vectorを返してくる関数
他の⽅法で解決する
define_static_array ではなく reflect_constant_array を使う。この
関数は配列に対する info を返すので、スプライスで取り出して構
造化束縛で受け、↓の形式で展開する。
template&lt;info E&gt;
void print_members() {
constexpr auto [...mems] =
[:meta::reflect_constant_array(
meta::members_of(E, meta::access_context::unchecked())
):];
template for (constexpr info mem: {mems...})
{ std::println(&quot;{}&quot;, meta::display_string_of(mem)); }
}
この形式の template for はパック展開 mems... の各要素に対して
std::println(…); を展開する。
��/��


# Page. 24

![Page Image](https://bcdn.docswell.com/page/V7NY4P8ME8.jpg)

使⽤例
��/��


# Page. 25

![Page Image](https://bcdn.docswell.com/page/YJ9PQ3KW73.jpg)

その1: 列挙型⇔⽂字列
template&lt;typename E&gt; requires std::is_enum_v&lt;E&gt;
constexpr E from_string(std::string_view s) {
constexpr auto etors =
std::define_static_array(meta::enumerators_of(^^E));
template for (constexpr info etor: auto(etors)) {
constexpr E val = meta::extract&lt;E&gt;(etor);
if (s == meta::identifier_of(etor)) return val;
}
throw std::out_of_range(&quot;out of range&quot;);
}
��/��


# Page. 26

![Page Image](https://bcdn.docswell.com/page/GJ8DGMRRJD.jpg)

その1: 列挙型⇔⽂字列 (続き)
template&lt;typename E&gt; requires std::is_enum_v&lt;E&gt;
constexpr std::string_view to_string(E e) {
constexpr auto etors =
std::define_static_array(meta::enumerators_of(^^E));
template for (constexpr info etor: auto(etors)) {
constexpr E val = meta::extract&lt;E&gt;(etor);
if (e == val) return meta::identifier_of(etor);
}
throw std::out_of_range(&quot;out of range&quot;);
}
��/��


# Page. 27

![Page Image](https://bcdn.docswell.com/page/LJLMG3K2ER.jpg)

その1: 列挙型⇔⽂字列 (続き)
enum struct color { red, green, blue };
int main() {
color e = from_string&lt;color&gt;(&quot;green&quot;);
assert(e == color::green);
std::println(&quot;{}&quot;, to_string(e));
}
��/��


# Page. 28

![Page Image](https://bcdn.docswell.com/page/47MYQD297W.jpg)

その2: 侵襲的なカスタム実装
struct hashable_marker {};
constexpr hashable_marker hashable{};
struct hasher_marker {};
constexpr hasher_marker hasher{};
struct [[=hashable]] point {
// …
std::size_t hash [[=hasher]]() const;
};
template&lt;typename T&gt;
requires /* Tはhashable_markerアノテーションを持つ */
std::hash&lt;T&gt; {
constexpr info hash_mem =
/* hasher_markerアノテーションを持つTのメンバーを探す */;
static std::size_t hash(T const &amp; x) const {
return x.[:hash_mem:]();
}
};
��/��


# Page. 29

![Page Image](https://bcdn.docswell.com/page/P7R984M9E9.jpg)

その3: キーワード引数
template&lt;typename ...Args&gt;
void f(Args &amp;&amp; ...args) {
kwarg_set aset { std::forward&lt;Args&gt;(args)..., arg&lt;&quot;z&quot;&gt; = 56 };
std::println(&quot;x={}, y={}, z={}&quot;, aset.x, aset.y, aset.z);
}
int main() {
f(arg&lt;&quot;x&quot;&gt; = 12, arg&lt;&quot;y&quot;&gt; = 34);
f(arg&lt;&quot;y&quot;&gt; = 34, arg&lt;&quot;x&quot;&gt; = 12);
f(arg&lt;&quot;z&quot;&gt; = 78, arg&lt;&quot;y&quot;&gt; = 34, arg&lt;&quot;x&quot;&gt; = 12);
}
指⽰付き初期化と違って順不同に指定できる。
��/��


# Page. 30

![Page Image](https://bcdn.docswell.com/page/PJXQ82V37X.jpg)

その4: コマンドラインパーサー
struct option {
std::string output
[[=short_name(&#039;o&#039;), =var(&quot;FILE&quot;), =help(&quot;output file&quot;)]];
bool verbose
[[=short_name(&#039;v&#039;), =help(&quot;verbose log&quot;)]];
};
というクラス定義から、
cmd --output hoge.txt --verbose
cmd -vo hoge.txt
cmd --help
Options:
-o, --output FILE output file
-v, --verbose
verbose log
-h, --help
show help and exit
のような形式のコマンドライン引数を解析できるパーサーを⽣
成する。
��/��


