C++26's ^^ cat ears operator lifts types into std::meta::info for reflection
Contents
C++26 adds an operator written ^^.
Because of how it looks, people have started calling it the “cat ears operator”.
If you read it from a Japanese perspective, ^^ looks a lot like an emoticon.
When ^^T suddenly shows up in your code, it feels a little like the compiler is smirking at you.
But what it actually does is much bigger than the silly name.
^^ is C++26’s static reflection operator: it converts types, functions, variables, and members into compile-time values.
In P3381R0, the original ^ proposal collided with Objective-C++‘s block extension, so ^^e came up as the alternative.
That paper proposes treating ^^ as a new single token.
The history in P2996R13 also records that, following the adoption of P3381R0, the reflection operator changed from ^ to ^^.
^^T makes a metadata value, not the type T itself
C++ can’t pass a type around as an ordinary value.
You can’t write f(int, float) to “pass the type int and the type float to a function”.
With static reflection you write ^^int or ^^std::string instead.
Adding ^^ gives you a std::meta::info value representing that type or declaration.
constexpr std::meta::info r = ^^int;
The wording in P2996R13 says ^^ is a unary operator whose result is a prvalue of type std::meta::info.
It isn’t a normal runtime value; it’s a reflection value meant to be handled at compile time.
Pass that reflection value to the std::meta functions and you can pull out enumerators, members, types, names, sizes, alignment, and more at compile time.
For example, in the paper’s own wording, enumerators_of(^^Color) reads as “the enumerators of Color”.
The judgment was that the target Color is easier to see this way than with a keyword form like reflectof(Color).
This runs in the opposite direction from the Project Detroit article, where Java is trying to embed V8 and CPython wholesale.
Project Detroit was about bringing another language’s runtime into the process.
C++26’s ^^ isn’t about a runtime at all; it’s about touching C++‘s own structure as a value at compile time.
[: :] goes from reflection value back to code
On its own, ^^ only gets you as far as converting a type or member into std::meta::info.
To use a reflection value as an actual type or name, the splice syntax [: :] comes in.
P2996R13 has an example that reflects S::type and splices it back as a type.
struct S { using type = int; };
void fn() {
typename [:^^S::type:] *var;
}
Here ^^S::type makes a reflection value for S::type.
[: ... :] splices that reflection value back into the source.
Because typename is attached, the result is treated as a declaration of an int* variable.
Once this combination is in the language, part of the “loop over a type list and generate code per type” work that used to live in template metaprogramming can be written as a standard language feature.
It isn’t the approach of holding member names as strings and looking them up at runtime.
It inspects C++ declarations at compile time and turns the result back into types or expressions.
^^ and [: :] together make a round trip: lift from code, inspect, and return to code.
flowchart TD
A["Type or declaration in code<br/>int / S::type / Color"] -->|"lift with ^^"| B["std::meta::info<br/>compile-time reflection value"]
B -->|"inspect with std::meta"| C["name, members, enumerators,<br/>and so on"]
C -->|"splice back"| D["back to code as a type or expression"]
^^ is the entrance from left to right, and [: :] is the exit from right to left.
Only the std::meta functions in the middle run at compile time; the reflection value itself doesn’t survive into the runtime code.
An enum-to-string example to see the whole thing
Where the round trip pays off most directly is turning an enum into a string.
In C++ so far, enumerator names don’t survive to runtime.
You had to hand-write a switch, duplicate the enum definition with an X-macro, or use a library like magic_enum that relies on the compiler’s internal representation.
In C++26 you can write it by just reflecting the enum and looping over its enumerators.
#include <experimental/meta>
#include <string>
enum class Color { red, green, blue };
template <typename E>
requires std::is_enum_v<E>
constexpr std::string enum_to_string(E value) {
template for (constexpr auto e : std::meta::enumerators_of(^^E)) {
if (value == [:e:]) {
return std::string(std::meta::identifier_of(e));
}
}
return "<unnamed>";
}
// enum_to_string(Color::green) is "green"
Step by step: ^^E turns the enum type E into a reflection value.
enumerators_of returns all of its enumerators as a sequence of std::meta::info.
template for is an expansion statement landing in C++26; it expands the loop body once per enumerator at compile time.
[:e:] splices the enumerator reflection value e back into the actual value (like Color::red), so it can be compared against value.
When they match, identifier_of(e) pulls out the enumerator’s name as a string.
Add an enumerator to the enum and the conversion side follows along without being touched.
No double-maintaining the definition, no external code generator.
What you can pull out of a type with std::meta
Beyond enumerators_of, std::meta has functions for inspecting structs and classes.
Pass them a reflection value and they return members, names, and types at compile time.
| Function | Returns |
|---|---|
enumerators_of(r) | the enumerators of an enum |
members_of(r) | all members of a type (data and functions) |
nonstatic_data_members_of(r) | only the non-static data members |
identifier_of(r) | the declaration’s name as a std::string_view |
type_of(r) | a reflection value for that declaration’s type |
parent_of(r) | a reflection value for the enclosing scope |
is_public(r) | whether the access is public |
To walk a struct’s fields into JSON, for instance, you take each field’s reflection value with nonstatic_data_members_of(^^T), get the key name with identifier_of, and read the value itself with obj.[:member:].
Serializers, ORMs, and config-file mappers can all be built on top of this kind of member walking.
That said, the function names and signatures are still in flux across P2996 revisions.
Code from older articles fairly often won’t compile as-is.
What it’s for
The use cases cluster around generating mechanical code from what’s already in a type’s definition.
Besides the enum and struct examples, command-line argument parsing, formatters, hash computation, and struct-to-struct conversion all show up among the P2996R13 examples.
Earlier C++ could already do similar things with macros, templates, external code generation, and custom IDLs.
The catch is that each of those is a mechanism separate from C++‘s own syntax.
^^ and [: :] do the “inspect a declaration” and “turn the result back into a type or expression” inside standard C++ syntax.
Of course, rather than ^^ showing up all over application code from day one, it’s more likely to be a feature library authors use internally.
P3381R0 also notes that reflection itself isn’t something beginners look at directly very often; it’s meant to carry the complicated work inside libraries.
Still an experimental implementation for now
Adoption into C++26 is settled, but as of 2026 it hasn’t landed in mainline GCC or Clang yet.
If you just want to try it, Compiler Explorer has experimental Clang/EDG forks where you can run ^^ and [: :] on the spot.
When you run it locally, the header is currently <experimental/meta>.
The standard one will be <meta>, but the experimental implementations tend to ship with the prefixed name.
The template for expansion statement is also a feature from a separate proposal, so reflection only becomes useful code in combination with the other new C++26 features, not on its own.
Weirdly named operators usually come from how they look
It’s not that ^^ is uniquely strange; programming languages have given symbols odd nicknames for a long time.
Even when the practical meaning is ordinary, a strong look makes the name stick first.
| Name | Language | Symbol | Roughly what it does |
|---|---|---|---|
| cat ears operator | C++26 | ^^ | turns a type or declaration into std::meta::info |
| spaceship operator | PHP, C++20, etc. | <=> | three-way comparison; returns less / equal / greater in one result |
| Elvis operator | Kotlin, etc. | ?: | uses the right side if the left is null |
| walrus operator | Python | := | assigns inside an expression and returns the value |
| safe navigation operator | Ruby, etc. | &. | skips the method call if the receiver is nil |
| splat operator | Ruby, etc. | * | expands an array into an argument list |
PHP’s migration 7.0 page describes <=> as the spaceship operator and says it returns -1, 0, or 1 as the comparison result.
Kotlin’s keywords and operators describes ?: as the Elvis operator that returns the right side when the left is null.
Python’s PEP 572 defines the NAME := expr assignment expression and explains that it came to be called the walrus operator during discussion.
Ruby’s calling methods docs introduce &. as the safe navigation operator.
The same page also describes * as the splat operator, the notation that turns an array into an argument list.
Lined up like this, ^^ is actually pretty tame.
If <=> is a spaceship, := is a walrus, and ?: is Elvis, then ^^ becoming cat ears was a natural next step.
The emoticon reading is probably stronger in Japan
Even in English ^^ is read as a visual symbol, but in Japan it’s deeply familiar as an emoticon from old chat and message boards.
For anyone who has seen it in contexts like thanks ^^, the ^^T glued onto a C++ type name looks oddly cheeky.
As syntax, ^^ isn’t a “slightly cute new operator”; it’s the entrance for pulling C++ types and declarations out as compile-time data.
Still, std::meta::info r = ^^int; really does look like it’s smiling at int.