Date: Sat, 15 Aug 2026 16:41:36 +0000
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];
}
};
```
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 16:41:46
