沿着类heirarchy

时间:2016-12-19 11:34:55

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

考虑下面的代码:

#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的不愉快?

1 个答案:

答案 0 :(得分: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那样被移位。