3

我认为如果BaseT是 DerivedT 的基础,那么“指向DerivedT 类型的 T 类成员的指针”可以用作“指向 BaseT 类型 T 的成员的指针”,这很简单。类比似乎至少对我来说是显而易见的,因为DerivedT * 可以用作BaseT *,所以DerivedT T:: * 应该能够用作BaseT T:: *

但事实并非如此:

struct BaseT
{
};

struct DerivedT: public BaseT
{
};

struct T 
{
    DerivedT m_test;
};

using BaseTMemPtr = BaseT T::*;

int main()
{
    T test;
    BaseT* simplePtr = &test.m_test; //It is DerivedT*, but can be used as BaseT*
    BaseT (T::*memPtr) = &T::m_test; //Error, BaseT T::* cannot be used as DerivedT T::*
    BaseTMemPtr memPtr2 = &T::m_test; //Error, just the same
}

正如我所见,有两种方法可以解释指向类成员的指针:

  1. DerivedT T:: * 是一个DerivedT指针,它指向T类对象内部的DerivedT对象(因此指向一个相对于另一个对象的对象)
  2. DerivedT T:: * 指向类T对象的某个部分,顺便说一下,它具有DerivedT类型。

所以这两种方式的主要区别在于,第一种可以解释为一种DerivedT指针(启用多态),而后一种丢弃了类型并限制了很多使用。

为什么 C++ 选择第二种方法?启用使用DerivedT T:: * 作为BaseT T:: *可能会产生什么不良后果?在实践中指向成员的指针是什么?

更新: 我想实现以下目标: 所需的解决方案 但如果成员不是 BaseMember 类型而是 BaseMember 后代,则它不起作用。如果我使用 BaseMembers,则该概念有效(但在这种情况下,我无法实现所需的成员功能): 适用于损坏的功能

更新 2:为什么
TLDR:
一种编译时间“标记”(唯一标识)运行时构造类的非静态成员对象的方法。然后检查一个常规(非成员)指针是否在具有
1的运行时函数中被编译时标记,标记成员的编译时间数组(可以是任何东西,在我看来是指向成员的多态指针)
2. 包含对象(具有标记和未标记成员)的“this”指针
3,指向非静态成员对象的常规(非指向成员)指针。

时间轴:类定义(编译时)->添加类成员(编译时)->将类成员标记为启用-例如在数组中-(编译时)->构造(运行时)->成员将调用注册函数(运行时) -> 在注册函数中,我们需要检查调用者(我们将其作为常规指针接收)是否允许调用此函数(运行时)。

详细描述:
在一个库中,我有一个 CRTP 基类(DataBinding),如果用户想使用它的编译和运行时功能,他们应该继承它。然后在库中我还有一个接口类:BaseMember,以及它的许多派生类。最终用户可以使用派生类在其用户定义的 DataBinding 子类中添加非静态类成员对象。

在用户代码中,在 DataBinding-descendant 用户类中,用户可以拥有基于 BaseMember 的非静态类成员。这里出现了需要指向成员多态性的新功能:用户应该能够在编译时标记一些基于 BaseMember 的类成员(!)(类本身没有 constexpr 构造函数) - 在我看来'mark' 可以存储指向 BaseMember 后代成员对象的成员的指针,并且只应允许标记的对象在 DataBinding(当前类的 CRTP 基)中运行时调用类成员函数(registerMember)。

在 registerMember 运行时函数中,我有“this”对象指针(包含对象),我有编译时用户定义的列表,它标记了启用的指向成员的指针(它可以用任何类型的唯一标识替换),我有实际的成员指针。我需要检查是否允许实际的成员指针调用该函数(它被标记为编译时间)。

4

1 回答 1

3

指向数据成员的指针通常由一个简单的整数值表示,告诉所有者类的开头到成员开头的偏移量。因此,在给定指向其所有者的指针的情况下检索数据成员的算法就像“将偏移量添加到地址并取消引用”一样简单。

然而,为了从指向派生的指针转到指向基址的指针,这样简单的描述是不够的。由于虚拟继承,偏移量不一定是恒定的。为了找到偏移量,我们需要一个实际的派生对象。偏移量存储在某处。所以在一般情况下,指向数据成员的指针必须表示为至少两个偏移量的组合,并且获取成员需要做出决定(是否存在虚拟基数?)

我猜这个标准可能只为非虚拟继承情况提供了这种转换。在这种情况下,偏移量是恒定的,并且转换将包括添加两个偏移量(除非我错过了其他一些极端情况)。然而,这并没有完成,原因当然是没有人觉得有足够的动力去写一份提案。指向数据成员的指针(与指向成员函数的指针相反)最初被 Stroustrup 认为是“泛化的产物,而不是真正有用的东西”(D&E,13.11 Pointers to Members)。它们现在(主要)用于以独立于实现的方式描述类布局,但实际上并没有必要以多态方式使用它们。

于 2021-10-16T08:01:03.413 回答