将指向派生类成员变量的指针转换为指向基类成员变量的指针,是否合法?

4
只要用于访问指向成员的指针的指针是正确类型,以下内容是否会导致未定义行为?
如果是这样,为什么需要强制转换?没有它会更漂亮(是的,我知道这只是个人观点)。
struct base {
    int foo(int base::* ptr) {
        return this->*ptr;
    }
};

struct sub : base {
    int blah{ 42 };
};

int main() {
    return sub{}.foo(static_cast<int base::*>(&sub::blah));
}

1
我不知道这里的形式,但它非常不安全,你可以在没有int成员的对象上调用foo,所以不要这样做。换句话说,知道它不是正式UB,如果它不是,对于真正的问题——类型不安全性并没有帮助,因此UB或不UB都相当无关紧要,除了作为语言律师问题(但如果这是您的兴趣,请考虑将问题标记为此类问题)。你试图通过这种方式解决什么问题? - Cheers and hth. - Alf
我还没有尝试解决问题。最近我开始使用一些大量使用它的代码,但这种模式让我感到非常不舒服。 - evan
1个回答

6
只要用于访问指向成员的指针的指针是正确类型,以下内容是否会导致未定义行为? 不会,它是良好形式的。规则来自[expr.mptr.oper]: 如果E1的动态类型不包含E2所引用的成员,则行为未定义。 *this的动态类型是sub,其中包含该成员,因此这是可以的。 那么我为什么需要转换? 因为这是一种固有的不安全转换,经验法则是固有的不安全操作应该是明显可见的。在这种特定情况下,它是好的,但那只是因为你很小心。需要强制进行转换,这迫使您必须考虑它。
一个更简单的例子是只看指针而不是指向成员的指针。在一个简单的继承体系中(假设是公共的、非模糊的等等),将Derived*转换为Base*总是安全的。这里没有任何问题,所以你不需要写一个转换。然而,将Base*转换为Derived*并不总是安全的...你可能没有一个Derived*。但是,它并不是完全不安全——完全禁止该转换是不好的。因此,安全转换是隐式的,但不安全的转换必须是显式的。

谢谢,我只能争论它的固有不安全性来改变它,因为我不能使用UB参数。 - evan
@evan 嗯?想知道改了什么? - Barry
我最近开始接触的代码。 - evan
@evan 如果你在使用时小心谨慎,它就不会不安全。你可以考虑使用一个模板成员函数来确保当前对象可以被dynamic_cast到成员指针的对象类型,并在失败时断言,然后调用真正的foo() -- 你可以在发布版本中禁用这个断言。 - cdhowie
@evan 这是我所说的一个例子。请注意,为了使这种方法起作用,base需要是多态的。这将至少在传入错误指针时引发大声失败,而不是UB。 - cdhowie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接