C++语言中有四种完全不相关的指针类型:对象指针、函数指针、非静态数据成员指针和非静态成员函数指针。 “指针”一词一般只适用于对象和函数指针类型[basic.compound]/3:
[...] 除了指向静态成员的指针,引用“指针”的文本不适用于指向成员的指针。 […]
指针和指向非静态成员的指针实际上被视为完全独立的两种复合类型[basic.compound]/1(这是有道理的,因为非静态成员指针更像是相对偏移,而不是实际地址)。
除了对象和函数指针之间有条件支持的转换,其语义(如果完全支持)将是实现定义的[expr.reinterpret.cast]/8,没有办法在这四种指针类型之间进行转换。
但是,该标准确实指定了对象指针 [expr.reinterpret.cast]/7 之间的相互转换、函数指针 [expr.reinterpret.cast]/6 之间的相互转换、数据成员指针 [expr.reinterpret.cast]/10.2 之间的相互转换以及成员函数指针 [expr.reinterpret.cast]/10.1 之间的相互转换。
因此,虽然一般来说没有与所有其他指针类型相关的通用指针类型,但将任何对象指针转换为某个任意对象指针类型并返回是明确定义的行为。将任何函数指针转换为某个任意函数指针类型并返回是明确定义的行为。将任何数据成员指针转换为某个任意数据成员指针类型并返回是明确定义的行为。将任何成员函数指针转换为某个任意成员函数指针类型并返回是明确定义的行为。所有这些不同类的指针类型的一个共同点是它们都是对象类型[basic.types]/8。
虽然这并不严格保证,例如,所有成员函数指针类型的大小相同,但它确实隐含地确定了某些成员函数指针类型的任何对象都可以有效地用于存储任何成员函数指针值。可能仍然存在比其他成员函数指针类型更大的成员函数指针类型,但它们不可能比其他成员拥有更多信息,因为标准要求与任何其他成员函数指针类型之间的转换不得丢失信息(始终可以恢复原始值) )。同样的论点适用于所有其他类的指针类型。
基于这一切,我认为在标准 C++ 中找到“最大的指针类型”在技术上是不可能的。然而,虽然在技术上可能无法找到最大的指针类型本身,但根据上面的论点,绝对有可能找到可靠存储任何指针类型值所需的存储量的上限。虽然这两者在技术上是不同的,但在实践中,第二个很可能几乎和第一个一样好(没有合理的编译器会随机添加大量填充位到某些指针类型的值表示中,因为这样做在技术上是合法的)。至少我很难想象除了存储指针值之外,您还可能想要对您所要求的信息进行哪些操作。
例如使用
using generic_obj_ptr = void*;
using generic_fun_ptr = void (*)();
class dummy_t;
using generic_dat_mem_ptr = dummy_t dummy_t::*;
using generic_mem_fun_ptr = void (dummy_t::*)();
你可以计算
auto obj_ptr_size = sizeof(generic_obj_ptr_t);
auto fun_ptr_size = sizeof(generic_fun_ptr_t);
auto dat_mem_ptr_size = sizeof(generic_dat_mem_ptr_t);
auto mem_fun_size = sizeof(generic_mem_fun_ptr_t);
auto max_ptr_size = std::max({ sizeof(generic_obj_ptr_t), sizeof(generic_fun_ptr_t), sizeof(generic_dat_mem_ptr_t), sizeof(generic_mem_fun_ptr_t) });
auto max_ptr_align = std::max({ alignof(generic_obj_ptr_t), alignof(generic_fun_ptr_t), alignof(generic_dat_mem_ptr_t), alignof(generic_mem_fun_ptr_t) });
或者直接使用
using ptr_storage_t = std::aligned_union<0U, generic_obj_ptr_t, generic_fun_ptr_t, generic_dat_mem_ptr_t, generic_mem_fun_ptr_t>;
甚至
using any_ptr_t = std::variant<generic_obj_ptr_t, generic_fun_ptr_t, generic_dat_mem_ptr_t, generic_mem_fun_ptr_t>;
或纯粹的形式:
using any_ptr_t = std::variant<void*, void (*)(), dummy_t dummy_t::*, void (dummy_t::*)()>;
作为存储,在与void*之间进行转换时可以存储任何对象指针值,在与void (*)()之间进行转换时可以存储任何函数指针值,在与void (*)()之间进行转换时可以存储任何数据成员指针。来自dummy_t dummy_t::*,并且在与void (dummy_t::*)()之间进行转换时可以存储任何成员函数指针。
play with it here
将其包装在一个类中,该类负责存储任何指针类型的任意值的所有转换(不要忘记处理可能的 cv 限定),应留给读者作为练习,主要是因为我今晚真的很想睡个好觉……