在这种情况下可以转换一个指向成员变量的指针吗?

发布于 2024-10-10 12:32:54 字数 1659 浏览 3 评论 0原文

我最近一直在刷新/更新我的 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++ 上按预期工作,我只是想确保我不是任何兼容的编译器都会因为这样做而受到影响。

I've been refreshing/updating my knowledge of C++ lately, and learning about strict aliasing has made me a bit wary of casting pointers of one type to another. I know that this following code sample works in practice on my compiler, but I want to make sure that it conforms to current standards:

#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;

}

Essentially, this simple tuple contains a pair of elements, each of which should derive from a common base class. The Get function returns a Base* to the member that the given index represents.

The key part that I'm wondering about is the reinterpret_casts. I know that casting from Derived Struct::* to Base Struct::* is generally a no-no, but in this case I only use the pointers-to-member-variable to get a pointer to the object. (I don't try to copy a derived object as though it were a base object, nor stuff a base object into a derived object's memory.) This works as intended on G++, and I just want to be sure that I'm not going to get bitten by any compliant compilers for doing this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

汐鸠 2024-10-17 12:32:54

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

要将指向基类的指针转换为派生类的指针,请使用dynamic_cast,当指向的对象不属于派生类时,它将返回NULL。如果您绝对确定该类是正确的,那么您可以使用 static_cast。

You should not use reinterpret_cast there. Actually you should not mention usage of reinterpret_cast anywhere where your goal is portability. reinterpret_cast is by definition something that has platform-specific results.

For casting a pointer to base into pointer of derived class use dynamic_cast, it will return NULL when the object pointed is not of derived class. If you are absolutely sure that the class is correct then you may use static_cast.

夜吻♂芭芘 2024-10-17 12:32:54

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

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

#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;

}

Use of reinterpret_cast is almost never portable. On top of that, the only valid use of pointer to member casts are the implicit cast from Type Derived::* to Type Base::* and careful uses of the static_cast from Type Base::* to Type Derived::*. Since you want to change the type of the member, not the type of the object containing members, this is neither of those.

How about putting tiny functions in that array instead of pointers to members? The following code is tested and should be entirely portable.

#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;

}
伏妖词 2024-10-17 12:32:54

编辑:参考标准。如果我没看错的话,因为您没有遇到任何异常,所以您所做的事情是未指定的,因此可能会或可能不会在任何特定编译器上工作。相关成员的类型没有任何例外。

从 5.2.10/9 (reinterpret_cast) 开始:

“指向成员的指针”类型的右值
类型T1”的X可以明确地
转换为类型的右值
“指向 T2 类型 Y 成员的指针”如果
T1 和 T2 都是函数类型或
两种对象类型。66) null 成员
指针值 (4.11) 转换为
的空成员指针值
目的地类型。这样的结果
转换未指定,除了
以下情况:

——转换
“指向成员的指针”类型的右值
函数”指向不同的指针
成员函数类型并返回其
原始类型产生原始类型
指向成员值的指针。

——转换
“指向数据的指针”类型的右值
T1 类型 X 的成员”到该类型
“指向 Y 类型数据成员的指针
T2”(其​​中对齐要求
T2的并不比那些更严格
T1) 并返回其原始类型
产生指向成员的原始指针
值。

EDIT: Reference from standard. If I'm reading it right, as you don't meet either of the exceptions, what you've done is unspecified and so may or may not work on any particular compiler. There aren't any exceptions for the type of the member being related.

From 5.2.10/9 (reinterpret_cast):

An rvalue of type “pointer to member
of X of type T1” can be explicitly
converted to an rvalue of type
“pointer to member of Y of type T2” if
T1 and T2 are both function types or
both object types.66) The null member
pointer value (4.11) is converted to
the null member pointer value of the
destination type. The result of this
conversion is unspecified, except in
the following cases:

— converting an
rvalue of type “pointer to member
function” to a different pointer to
member function type and back to its
original type yields the original
pointer to member value.

— converting
an rvalue of type “pointer to data
member of X of type T1” to the type
“pointer to data member of Y of type
T2” (where the alignment requirements
of T2 are no stricter than those of
T1) and back to its original type
yields the original pointer to member
value.

我最亲爱的 2024-10-17 12:32:54

C 风格的cast 总是比reinterpret_cast 更好。

如果C风格的转换工作,它是独立的有效平台。

始终避免reinterpret_cast。

编辑

我的意思是,使用reinterpret_cast你可能会指向错误的内存地址,C风格的转换处理所有与平台相关的问题,例如ABI,内存对齐,指针大小等。

编辑
受评论者的启发,我阅读了 ISO/IEC 14882:2003 第 5.2.10 节“Reinterpret_cast”。

当然,我的理解是有限的,但让我想起为什么我首先讨厌reinterpret_cast。

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

如果强制转换操作数是具有复杂继承层次结构的类的实例指针(例如ATL/COM类),则一个reinterpret_cast足以杀死您的进程并产生难以理解的错误。

我们可以使用 C 风格的强制转换,但对后面的实际强制转换操作有模糊的了解。但我们必须真正了解确切的细节才能安全地使用reinterpret_cast。

C style cast is always better than reinterpret_cast.

If C style cast work, it is valid platform independently.

Always avoid reinterpret_cast.

EDITED

I mean, with reinterpret_cast you could point wrong memory address, C style cast handle all the platform related issues such as ABI, memory align, pointer size, etc.

EDITED
By the inspiration of commentators, I've read ISO/IEC 14882:2003 section 5.2.10 "Reinterpret_cast".

Of course my comprehension is limited, but it strikes me to remember why I hated reinterpret_cast in the first place.

I think, reinterpret_cast is lack of or has very limited awareness of inheritance hierarchy.

If cast operand is a instance pointer of class which has complicated inheritance hierarchy (such as ATL/COM classes), one reinterpret_cast is enough to kill you process with incomprehensible errors.

We can use C style cast with vague knowledge of actual cast operation behind. But we must really know exact detail to use reinterpret_cast safely.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文