可以在这个实例中转换指向成员变量的指针吗?

时间:2011-01-03 05:54:44

标签: c++ pointers casting member strict-aliasing

我最近一直在刷新/更新我对C ++的知识,而且学习严格的别名使我有点担心将一种类型的指针转​​换为另一种类型。我知道以下代码示例在我的编译器上实际运行,但我想确保它符合当前标准:

#include <iostream>

using namespace std;

class MyBase {

    public:
    virtual void DoSomething() = 0;

};

class MyDerived1 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #1" << endl;
    }

};

class MyDerived2 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #2" << endl;
    }

};

template <typename Base, typename Member1, typename Member2>
struct Tuple {

    public:
    Base* Get(int i) {
        return &(this->*(lookupTable[i]));
    }

    private:
    Member1 member1;
    Member2 member2;

    static Base Tuple::* const lookupTable[2];

};

template <typename Base, typename Member1, typename Member2>
Base Tuple<Base, Member1, Member2>::* const Tuple<Base, Member1, Member2>::lookupTable[2] = {
    reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member1),
    reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member2)
};

int main() {

    Tuple<MyBase, MyDerived1, MyDerived2> tuple;

    tuple.Get(0)->DoSomething();
    tuple.Get(1)->DoSomething();

    return 0;

}

本质上,这个简单的元组包含一对元素,每个元素都应该来自一个公共基类。 Get函数将Base*返回给定索引所代表的成员。

我想知道的关键部分是reinterpret_casts。我知道从Derived Struct::*Base Struct::*的转换通常是禁止的,但在这种情况下我只使用指向成员变量的指针来获取指针到对象。 (我不会尝试将派生对象复制为基础对象,也不会将基础对象复制到派生对象的内存中。)这在G ++上按预期工作,我只是想确定我不是任何兼容的编译器都会为此感到厌烦。

4 个答案:

答案 0 :(得分:2)

你不应该在那里使用reinterpret_cast。实际上你不应该在你的目标是可移植性的任何地方提到reinterpret_cast的使用。根据定义,reinterpret_cast具有特定于平台的结果。

为了将指向base的指针转换为派生类的指针,使用dynamic_cast,当指向的对象不是派生类时,它将返回NULL。如果您完全确定该类是正确的,那么您可以使用static_cast。

答案 1 :(得分:1)

reinterpret_cast的使用几乎从不可移植。最重要的是,对成员强制转换的指针的有效使用是从Type Derived::*Type Base::*的隐式强制转换,并谨慎使用static_cast来自Type Base::* 1}}到Type Derived::*。由于您要更改成员的类型,而不是包含成员的对象的类型,因此这两者都不是。

如何将微小的函数放在该数组中而不是指向成员的指针?以下代码经过测试,应完全可移植。

#include <iostream>

using namespace std;

class MyBase {

    public:
    virtual void DoSomething() = 0;

};

class MyDerived1 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #1" << endl;
    }

};

class MyDerived2 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #2" << endl;
    }

};

template <typename Base, typename Member1, typename Member2>
struct Tuple {

    public:
    Base* Get(int i) {
        return &(this->*lookupTable[i])();
    }

    private:
    Member1 member1;
    Member2 member2;

    template <typename MemType, MemType Tuple::*member>
    Base& GetMember() { return this->*member; }

    typedef Base& (Tuple::*get_member_func)();
    static const get_member_func lookupTable[2];

};

template <typename Base, typename Member1, typename Member2>
const typename Tuple<Base, Member1, Member2>::get_member_func
Tuple<Base, Member1, Member2>::lookupTable[2] = {
    &Tuple::GetMember<Member1, &Tuple::member1>,
    &Tuple::GetMember<Member2, &Tuple::member2>
};

int main() {

    Tuple<MyBase, MyDerived1, MyDerived2> tuple;

    tuple.Get(0)->DoSomething();
    tuple.Get(1)->DoSomething();

    return 0;

}

答案 2 :(得分:0)

编辑:参考标准。如果我正确地阅读它,因为你没有遇到任何一个例外,你所做的是未指定的,因此可能或可能不适用于任何特定的编译器。相关成员的类型没有任何例外。

从5.2.10 / 9(reinterpret_cast):

  

“指向成员的指针”的右值   “T1类型的X”可以明确地表示   转换为类型的右值   “如果指向T2的Y成员的指针”   T1和T2都是功能类型或   两种对象类型.66)null成员   指针值(4.11)被转换为   null的成员指针值   目的地类型。结果如此   转换未指定,但在   以下情况:

     

- 转换   rvalue类型“指向成员的指针   函数“指向不同的指针   成员函数类型并返回其中   原始类型产生原始   指向成员价值的指针。

     

- 转换   “数据指针”类型的右值   类型为T1的X的成员   “指向Y类型数据成员的指针   T2“(对齐要求   T2没有比那些更严格   T1)并恢复其原始类型   产生指向成员的原始指针   值。

答案 3 :(得分:-1)

C样式演员总是比reinterpret_cast好。

如果C样式投射工作,它是独立的有效平台。

始终避免reinterpret_cast。

<强> EDITED

我的意思是,使用reinterpret_cast你可以指出错误的内存地址,C样式转换处理所有平台相关的问题,如ABI,内存对齐,指针大小等。

<强> EDITED 在评论员的启发下,我阅读了ISO / IEC 14882:2003第5.2.10节“Reinterpret_cast”。

当然,我的理解是有限的,但是让我记住为什么我一开始就讨厌reinterpret_cast。

我认为,reinterpret_cast缺乏或者对继承层次结构的认识非常有限。

如果强制转换操作数是具有复杂继承层次结构的类的实例指针(例如ATL / COM类),则一个reinterpret_cast就足以使用难以理解的错误来终止进程。

我们可以使用C风格演员,模糊背后的实际演员操作知识。但我们必须真正了解确切的细节才能安全地使用reinterpret_cast。