Tech 9 min read

C++26's ^^ cat ears operator lifts types into std::meta::info for reflection

IkesanContents

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.

FunctionReturns
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.

NameLanguageSymbolRoughly what it does
cat ears operatorC++26^^turns a type or declaration into std::meta::info
spaceship operatorPHP, C++20, etc.<=>three-way comparison; returns less / equal / greater in one result
Elvis operatorKotlin, etc.?:uses the right side if the left is null
walrus operatorPython:=assigns inside an expression and returns the value
safe navigation operatorRuby, etc.&.skips the method call if the receiver is nil
splat operatorRuby, 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.