【问题标题】:"Derived pointer to member" to "base pointer to member" error“指向成员的派生指针”到“指向成员的基指针”错误
【发布时间】:2021-12-03 18:30:41
【问题描述】:

为了支持一些编译时魔法,我想使用指向成员的指针,例如:

struct BaseT
{
};

struct DerivedT: public BaseT
{
};

struct TestT 
{
    DerivedT testMem;

    typedef BaseT (TestT::* TestTMemPtr);

    constexpr TestT() = default;

    static constexpr TestTMemPtr testMemOffset()
    {
        return &TestT::testMem;
    }
};

int main()
{
    constexpr TestT test;
}

我无法将指向派生成员的指针作为指向基成员的指针返回,我通过 clang 得到了这个:

cannot initialize return object of type 'TestT::TestTMemPtr' (aka 'BaseT (TestT::*)') with an rvalue of type 'DerivedT TestT::*'

我用 gcc 查了一下:

error: invalid conversion from 'DerivedT TestT::*' to 'TestT::TestTMemPtr' {aka 'BaseT TestT::*'}

这是正常行为吗?我想我总是可以使用派生指针作为基指针。

更新: 好吧,原来的例子不是最好的,我觉得这个更有表现力,所以DerivedT*可以用作BaseT*,但DerivedT TestT::*不能用作BaseT TestT::*

struct BaseT
{
};

struct DerivedT: public BaseT
{
};

struct TestT 
{
    DerivedT m_test;
};

using BaseTMemPtr = BaseT TestT::*;

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

【问题讨论】:

  • 无关的,using TestTMemPtr = BaseT TestT::* ; 而不是那个丑陋的typedef
  • 显然它不是生产代码,但你是对的 :)

标签: c++ c++11 metaprogramming compile-time


【解决方案1】:

从继承的角度来看,BaseT TestT::*DerivedT TestT::* 是两种不相关类型¹,所以不能从后者初始化前者,也不能反之反之亦然,就像你不能用 double* 初始化 int* 因为 intdouble 不是基类和派生类。


¹ 我的意思是这些类型的两个对象不指向两个类,它们是另一个类的基础。 BaseT TestT::*DerivedT TestT::* 都是指针类型,但它们不指向两个类,其中一个是另一个的基类;他们甚至不首先指向类(参见下面的演示代码),因此所指向的类型之间不可能存在继承关系,因为继承是之间的事情,而不是types 泛指,比如成员函数类型。

#include <type_traits>
struct BaseT {};

struct DerivedT: public BaseT {};

struct TestT {};

template<typename T, typename = void>
struct points_to_class : std::false_type {};
template<typename T>
struct points_to_class<T*> : std::is_class<T> {};

static_assert(points_to_class<BaseT*>::value); // passes
static_assert(points_to_class<BaseT TestT::*>::value); // fails

但是,只有指针都指向类并且这两个类是通过继承相关的,才能实现指针之间的转换?

好吧,如果你看一下Pointer declaration page on cppreference.com,它确实有一个关于指向成员函数的指针的部分,它是关于指向成员函数的指针之间的转换。

但它是关于指向基类的成员函数的指针到指向派生类的相同成员函数的指针,而您似乎希望将 指向成员函数的指针(TestT返回基类 (BaseT) 指向同一类 (TestT) 的成员函数的指针 返回派生类 (DerivedT)。同样,这两种类型是不相关的。

【讨论】:

  • 请查看更新后的示例。 int * i; double * d{i}; 显然是错误的,但 DerivedT* x; BaseT* y{x}; 是一个更好的例子,它可以工作。
  • @Broothy,BaseTDerivedT 的基,但BaseT TestT::* 不是DerivedT TestT::* 的基。
  • 也许是我的误解,但是&TestT::m_test 是什么? DerivedT 类型的成员指针。如果我可以将 DerivedT 指针用作 BaseT 指针,那么将 DerivedT 成员指针用作 BaseT 成员指针有什么问题?对我来说,这个类比是显而易见的。
  • @Broothy,我已经简化了这个问题,因为它肯定需要更复杂。你现在怎么看?
  • 函数和对象在整个标准中的处理方式都不同。整个函数占用空间是函数“类型”的一部分,并且没有像 DerivedT* 是 BaseT* 这样的关系。在成员指针(而不是成员函数指针)上,标准可以允许相同而不会产生任何后果。但是现在我可以理解指向成员的概念了:它并不指向对象内部的对象(DerivedT*),而是指向TestT对象的一部分(顺便说一下,该成员具有DerivedT类型)。所以多态性不能工作,因为指针不指向 DerivedT* 事物只是 TestT obj 的一部分
猜你喜欢
  • 2018-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多