C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Flat Member Pointer for Indirect Non-static Members

From: Jens Maurer <jens.maurer_at_[hidden]>
Date: Sat, 15 Aug 2026 19:27:40 +0200
In which way does your proposal have overlap with the following
paper; would it solve similar problems?

Generalised member pointers (Jeff Snyder)
https://wg21.link/p0149r3

Jens


On 8/15/26 18:41, SD SH via Std-Proposals wrote:
> 1. Abstract
>
> The purpose of this draft is to extend member pointers to allow them directly refer to indirect non-static members, called `flat member pointer`,
> with two features:
>
> a) Add `operator .` and `operator .*` for member pointers to create flat member pointers.
>
> b) Add function template
> ```
> template<std::size_t I, tuple-like T>
> constexpr auto get_member_pointer() noexcept -> std::tuple_element_t<I, T> T::*;
> ```
> to obtain a member pointer to the `I`-th element of `T`.
> (be rejected if `std::is_reference_v<std::tuple_element_t<I, T>> == true`)
>
> 2. Motivation
>
> Simply, the purpose is to create `D A::*` refers to `b.d1` or `c.d2` or `d3`, the members of different classes:
> ```
> A {
> B b {
> D d1; // B::D
> Z;
> };
> C c {
> D d2; // C::D
> };
> D d3; // A::D
> };
> ```
>
> Consider this case:
> ```
> import std;
>
> template<typename T>
> struct A {
> T t;
> bool value;
> };
> template<typename... Ts>
> struct X {
> std::tuple<A<Ts>...> data;
> std::size_t active_index;
> bool active_value() {
> return /* std::get<active_index>(data).value */;
> }
> };
> ```
> `std::get<active_index>(data).value` is typically implemented with an array of function pointers that return each `A::value` of `data`,
> which hinders optimization due to indirect calls.
> For this case, the offsets of all `A::value` are known in compile time,
> but `bool A<T>::*` and `bool A<U>::*` cannot be converted to one another, so using array of member pointers doesn't work either.
>
> Similarly, because type packs cannot be expanded directly as members, the elements cannot all be direct members of `std::tuple`,
> which prevents member pointers for all members from being obtained, even though elements are unnamed.
>
> If member pointers can directly refer to indirect non-static members, these problems will be solved.
>
> Because elements are unnamed, we use `std::get_member_pointer` to get member pointers:
> ```
> template<std::size_t I, tuple-like T>
> constexpr auto get_member_pointer() noexcept -> std::tuple_element_t<I, T> T::*;
> ```
>
> 3. Syntax
>
> a) member-pointer-expression.non-static-member-name
> b) member-pointer-expression.*member-pointer-expression
>
> Written as
> a) (&Class_Type::member_of_class).final_member
> b) (&Class_Type::member_of_class).*(&Member_Type_of_Class::final_member)
> below.
>
> 4 Semantics
>
> assume there are:
> ```
> auto class2mbr = &Class_Type::member_of_class;
> auto mbr2final = &Member_Type_of_Class::final_member;
> ```
> then
> `class_object.*(class2mbr.*mbr2final)`
> is equivalent to
> `class_object.*class2mbr.*mbr2final`
> and
> `class_object.member_of_class.final_member`
>
> A simple example:
> ```
> // built-in, member pointer to offset
> constexpr std::size_t to_offset(member-pointer-type auto&&) noexcept;
> // built-in, offset to member pointer
> template<member-pointer-type T>
> constexpr T to_mbrptr(std::size_t) noexcept;
>
> // internal implement
> // `(&Class_Type::member_of_class).*(&Member_Type_of_Class::final_member)` is equivalent to:
> template<typename Class_Type, typename Member_Type_of_Class, typename Final_Member_Type>
> constexpr Final_Member_Type Class_Type::*
> operator .*(Member_Type_of_Class Class_Type::* outer, Final_Member_Type Member_Type_of_Class::* inner) noexcept {
> return to_mbrptr<Final_Member_Type Class_Type::*>(to_offset(outer) + to_offset(inner));
> }
> ```
>
> 5. Example
>
> ```
> import std;
>
> struct Z {
> void foo() {
> std::cout << "access Z at " << this;
> }
> };
> struct Y {
> Z z;
> };
> struct X {
> Y y;
> };
>
> int main() {
> constexpr auto x_y = &X::y;
> constexpr auto x_y_z_mbr = (&X::y).z;
> constexpr auto x_y_z_mbrptr = x_y.*(&Y::z);
> static_assert(x_y_z_mbr == x_y_z_mbrptr);
>
> X x { };
> (x.*x_y_z_mbr).foo();
> }
> // output:
> // access Z at 0x0000001E045F3010
> ```
>
> For the case which was mentioned in [2. Motivation],
> we create an array of `bool X::*`, refers to each `A::value`, and access `data` through them:
> ```
> // ...
> template<typename... Ts>
> struct X {
> // ...
> static constexpr std::array<bool X::*, sizeof...(Ts)> each_value =
> []<std::size_t... Is>(std::index_sequence<Is...>) -> std::array<bool X<Ts...>::*, sizeof...(Ts)> {
> return { (&X::data).*std::get_member_pointer<Is, std::tuple<A<Ts>...>>().*(&A<Ts>::value)... };
> }(std::make_index_sequence<sizeof...(Ts)>());
> bool active_value() {
> return data.*each_value[active_index];
> }
> };
> ```

Received on 2026-08-15 17:27:50