std::is_poi...e_with_class(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’… std::is_pointer_interconvertible_with_class β€” C++ Standard Library, bestie

Okay so this one is from the C++ Standard Library, queen of the <type_traits> header, and it's the compile-time trait that asks the big question: is this pointer-to-member thing actually sitting at the same address as the whole object? Let's get into it πŸŽ€


✨ NAME ✨

std::is_pointer_interconvertible_with_class β€” and yeah, it's said twice in the original, so I'm saying it twice too: std::is_pointer_interconvertible_with_class. Iconic, repetitive, slay πŸ’…


🌸 SYNOPSIS 🌸

Defined in header <type_traits>:

template< class S, class M >
constexpr bool is_pointer_interconvertible_with_class( M S::* mp )
  • since: C++20
  • noexcept: yes babe, it's noexcept

So here's the vibe: given an object s of type S, this thing works out whether s.*mp refers to a subobject of s and whether s is pointer-interconvertible with its subobject s.*mp.

⚠️ Real talk though: the program is ill-formed if S is not a complete type. Not "returns false", not "warns you" β€” ill-formed. Periodt πŸ’€

And also, listen carefully because this is the important bit: if S is not a StandardLayoutType, or M is not an object type, or mp is equal to nullptr, the result is always false. No surprises, no maybe, just false 🫢


πŸ¦‹ PARAMETERS πŸ¦‹

  • mp β€” a pointer-to-member to detect. That's the one and only thing you're handing it πŸ”

🌈 RETURN VALUE 🌈

It gives you true if s.*mp refers to a subobject of s and s is pointer-interconvertible with its subobject s.*mp. Otherwise you get false.

And where's s from? s is an object of type S. Keeping the receipts πŸ“


πŸ‘‘ NOTES πŸ‘‘ (the lore drop)

Okay, this part is lowkey the most important, so pay attention bestie 🧠✨

The type of a pointer-to-member expression &S::m is not always M S::*, where m is of type M β€” because m may be a member inherited from a base class of S. So the type can surprise you. That's exactly why you can specify the template arguments explicitly, to avoid potentially surprising results. Do it. Trust πŸ’…

Then there's this beautiful little fact: if there is a value mp of type M S::* such that std::is_pointer_interconvertible_with_class(mp) == true, then reinterpret_cast<M&>(s) has a well-defined result and it refers to the same subobject as s.*mp, where s is a valid lvalue of type S. Well-defined. We love to see it πŸ₯Ή

And on common platforms, the bit pattern of mp is all zero if std::is_pointer_interconvertible_with_class(mp) == true. Cute little implementation detail, iykyk πŸŽ€

🌟 Feature-test macro

Here's the feature-test macro situation, and yes I'm keeping every cell:

  • Macro: __cpp_lib_is_pointer_interconvertible
  • Value: 201907L
  • Std: (C++20)
  • Feature: Pointer-interconvertibility traits:
    • std::is_pointer_interconvertible_base_of
    • std::is_pointer_interconvertible_with_class

πŸ’– EXAMPLE πŸ’–

// Run this code

#include <type_traits>

struct Foo { int x; };
struct Bar { int y; };

struct Baz : Foo, Bar {}; // not standard-layout

static_assert( not std::is_same_v<decltype(&Baz::x), int Baz::*> );
static_assert( std::is_pointer_interconvertible_with_class(&Baz::x) );
static_assert( not std::is_pointer_interconvertible_with_class<Baz, int>(&Baz::x) );

int main() { }

Look at those static_asserts go β€” the first one proving that decltype(&Baz::x) is not int Baz::* (told you it gets weird), the second one passing, and the third one failing once you spell out the template arguments. Chef's kiss πŸ”₯


πŸ” SEE ALSO πŸ”

  • is_standard_layout (C++11) β€” checks if a type is a standard-layout type (class template)
  • is_member_object_pointer (C++11) β€” checks if a type is a pointer to a non-static member object (class template)

Anyway bestie, that's the whole page β€” sourced from http://cppreference.com, dated 2024.06.10, and filed under std::is_poi...e_with_class(3). Go be pointer-interconvertible with your subobjects, ily πŸ«ΆπŸ’œβœ¨

πŸ“¦ where this page comes from