<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra" -->

---
title: Beautiful Type Erasure with C++26 Reflection | daily.dev
description: rjk::duck is a C++26 library that uses static reflection to implement ergonomic type erasure without boilerplate. The post walks through how C++26 annotations,...
canonical: https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Beautiful Type Erasure with C++26 Reflection | daily.dev
og:description: rjk::duck is a C++26 library that uses static reflection to implement ergonomic type erasure without boilerplate. The post walks through how C++26 annotations,...
og:url: https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra
og:image: https://api.daily.dev/og/posts/sUnPGgsra.png
og:image:alt: Beautiful Type Erasure with C++26 Reflection
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Beautiful Type Erasure with C++26 Reflection

**[Hacker News](https://daily.dev/sources/hn)** · 19 min read · 1 upvotes · 0 comments

## Summary

rjk::duck is a C++26 library that uses static reflection to implement ergonomic type erasure without boilerplate. The post walks through how C++26 annotations, consteval blocks, and the splice operator are used to generate vtables at compile time, resolve overloads via generated overload sets, and expose clean member-function syntax through pointer-interconvertibility tricks. It also covers a performance tuning mechanism that inlines hot function pointers directly into the duck object to avoid cold vtable loads.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://ryanjk5.github.io/posts/rjk-duck>

## Questions this post answers

### How can I implement type erasure in C++26 using reflection instead of writing boilerplate vtable code by hand?

Define an interface as a struct annotated with a custom trait annotation, then use C++26 reflection functions like members_of, annotations_of, and the consteval block with define_aggregate to generate a vtable struct automatically. Function pointers are filled in using the splice operator, and overload resolution is handled by generating a compiler-resolved overload_set rather than manually reimplementing C++ overload rules.

_Developers exploring C++26 reflection for type erasure can find implementation patterns and updates through daily.dev._

### How do I avoid storing a back-pointer to the owning object in each generated wrapper function without bloating the object's size?

Use pointer-interconvertibility: since a struct and its first standard-layout data member share the same memory address, a reinterpret_cast from the member's this pointer back to the enclosing wrapper type recovers the owner without storing an explicit pointer. This keeps sizeof(duck) constant regardless of how many trait member functions are generated, unlike a naive design that stores one owner pointer per function.

_Anyone optimizing object layout in C++ can track techniques like this through daily.dev._

### Does C++26 reflection currently work at compile time for type-erased objects that require casting through void*?

Not fully, because reinterpret_cast is disallowed at compile time and void* can only be cast back to its exact original type, not a convertible one, which breaks the pointer-interconvertibility trick used for type erasure. However, gcc-trunk currently permits casting through void* at compile time as an implementation quirk, so constexpr use is possible for now but not guaranteed to remain standard-compliant.

_Developers testing C++26 constexpr and reflection limits can follow compiler-specific behavior via daily.dev._

## Similar posts on daily.dev

- [C\+\+ Reflection: a Universal Printer](https://daily.dev/posts/c-reflection-a-universal-printer-pmscycfou) · C\+\+ · 1 upvotes · 0 comments
- [More C\+\+26 reflection at compile-time](https://daily.dev/posts/more-c-26-reflection-at-compile-time-xhruabdtu) · Andreas Fertig · 5 upvotes · 0 comments
- [ReSharper C\+\+ 2026.2: C\+\+26 Reflection, ISPC Language Support, And More](https://daily.dev/posts/resharper-c-2026-2-c-26-reflection-ispc-language-support-and-more-gccikz3cd) · JetBrains · 0 upvotes · 0 comments
- [C\+\+26 Reflection gives us universal template parameters](https://daily.dev/posts/c-26-reflection-gives-us-universal-template-parameters-rzixrpk5v) · Arthur O'Dwyer · 2 upvotes · 0 comments

---

Tags: [#c++](https://daily.dev/tags/c++)

[View this post on daily.dev](https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Beautiful Type Erasure with C++26 Reflection","url":"https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra"},"datePublished":"2026-07-14T14:48:51.534Z","dateModified":"2026-09-13T21:29:49.029Z","description":"rjk::duck is a C++26 library that uses static reflection to implement ergonomic type erasure without boilerplate. The post walks through how C++26 annotations,...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/b7d451bbc20a3e331f6f6358fe83df6c?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/b7d451bbc20a3e331f6f6358fe83df6c?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Hacker News","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Hacker News","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/hn","url":"https://daily.dev/sources/hn"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"c++","timeRequired":"PT19M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Hacker News","item":"https://daily.dev/sources/hn"},{"@type":"ListItem","position":3,"name":"Beautiful Type Erasure with C++26 Reflection"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/beautiful-type-erasure-with-c-26-reflection-sunpggsra#faq","mainEntity":[{"@type":"Question","name":"How can I implement type erasure in C++26 using reflection instead of writing boilerplate vtable code by hand?","acceptedAnswer":{"@type":"Answer","text":"Define an interface as a struct annotated with a custom trait annotation, then use C++26 reflection functions like members_of, annotations_of, and the consteval block with define_aggregate to generate a vtable struct automatically. Function pointers are filled in using the splice operator, and overload resolution is handled by generating a compiler-resolved overload_set rather than manually reimplementing C++ overload rules. Developers exploring C++26 reflection for type erasure can find implementation patterns and updates through daily.dev."}},{"@type":"Question","name":"How do I avoid storing a back-pointer to the owning object in each generated wrapper function without bloating the object's size?","acceptedAnswer":{"@type":"Answer","text":"Use pointer-interconvertibility: since a struct and its first standard-layout data member share the same memory address, a reinterpret_cast from the member's this pointer back to the enclosing wrapper type recovers the owner without storing an explicit pointer. This keeps sizeof(duck) constant regardless of how many trait member functions are generated, unlike a naive design that stores one owner pointer per function. Anyone optimizing object layout in C++ can track techniques like this through daily.dev."}},{"@type":"Question","name":"Does C++26 reflection currently work at compile time for type-erased objects that require casting through void*?","acceptedAnswer":{"@type":"Answer","text":"Not fully, because reinterpret_cast is disallowed at compile time and void* can only be cast back to its exact original type, not a convertible one, which breaks the pointer-interconvertibility trick used for type erasure. However, gcc-trunk currently permits casting through void* at compile time as an implementation quirk, so constexpr use is possible for now but not guaranteed to remain standard-compliant. Developers testing C++26 constexpr and reflection limits can follow compiler-specific behavior via daily.dev."}}]}
```

