【问题标题】:Function pointer coercion along a class heirarchy沿类层次结构的函数指针强制
【发布时间】:2017-05-04 10:51:23
【问题描述】:

考虑下面的代码:

#include <type_traits>
#include <iostream>

struct B {
    virtual const char* whoami() const { return "I am a B!\n"; };
};

struct D : B {
    const char* whoami() const override { return "I am a D!\n"; };
};

void foo_impl( B*(*pf)() )
{
    B* b = pf();
    std::cout << b->whoami();
}

template <class C>
auto foo( C*(*pf)() ) -> std::enable_if_t<std::is_base_of<B, C>::value> {
    foo_impl(reinterpret_cast<B*(*)()>(pf));
}

D *bar() {
    static D d_; // kludge to work around dangling pointer problem.
                 // Unrelated to the actual question
    return &d_;
}

int main() {
    foo(bar); // prints "I am a D!" on gcc 5.1
    return 0;
}

函数指针当然不能按照标准强制,也许没有必要(我们总是可以只返回一个B*),但请幽默。据我所知,没有可能违反 LSP,所以如果可能的话,可以使用返回 D* 的函数以完全不透明的方式代替返回 B* 的函数。

foo 模板执行我希望编译器执行的静态类型检查,然后将类型信息抛到脑后。在这一点上,我知道除非我转换回原始指针类型,否则行为是未定义的(C++11 §5.2.10/6)。

因此我的问题是:

是否存在与标准无关的实际原因会导致上述代码失败?或者有没有其他的标准参考,可以缓解上面代码中UB的不愉快?

【问题讨论】:

    标签: c++ undefined-behavior type-coercion


    【解决方案1】:

    您帖子的以下陈述不正确:

    可以使用返回 D* 的函数代替返回的函数 B* 完全不透明

    这不是真的,因为将D* 转换为B* 不仅仅是一个转换:可能需要转换地址。它没有在您的示例中,因为您不使用多重继承。

    考虑这个例子:

    #include <type_traits>
    #include <iostream>
    
    struct A {
      int i = 0;
      int whoami() const { return i; };
    };
    
    struct B {
      int j = 1;
      int whoami() const {
        std::cout << "Woohoo B is called.\n";
        return j;
      };
    };
    
    struct D : A, B {
    };
    
    void foo_impl(B *(*pf)()) {
      B *b = pf();
      char const * identity = (0 == b->whoami() ? "A" : "B");
      std::cout << "I am " << identity << std::endl;
    }
    
    template <class C>
    auto foo( C*(*pf)() ) -> std::enable_if_t<std::is_base_of<B, C>::value> {
          foo_impl(reinterpret_cast<B*(*)()>(pf));
    }
    
    D *bar() {
      static D d_;
      return &d_;
    }
    
    int main() {
      foo(bar);
      return 0;
    }
    

    这会打印出I am A,即使您认为自己使用了B。调用的函数实际上是B::whomai,但下面的数据成员是来自A 的那个,因为地址没有像指针的正确static_cast 那样移动。

    【讨论】:

    • 没错,我没有考虑多重继承。我不会更新我的问题,以免您的答案过时。但我觉得还不够。
    • 嗯,这是一个原因。可能还有其他我没有想到的,但对我来说,它的盐是:指针还没有被 static_casted。
    • 是的,这确实是一个重要的原因,总是按预期返回。而且您的回答非常棒,如果没有人对单一继承案例提出意见,我可能会接受它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 2014-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多