【问题标题】:Change the meaning of code by replacement of forward declaration with include通过将前向声明替换为包含来更改代码的含义
【发布时间】:2015-05-04 14:17:43
【问题描述】:

Google C++ Style Guide 声明

在极端情况下,将 #include 替换为前向声明可以默默地改变代码的含义。

你能帮我找一些例子吗?

【问题讨论】:

  • 我想不出任何将一个有效程序变成另一个具有不同含义的有效程序的情况。但它很容易导致程序默默地表现出未定义的行为。例如。 "5.3.5/5 如果要删除的对象在删除点具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为未定义。"跨度>
  • @IgorTandetnik 您能否将您的评论转换为答案,最好使用一个小代码示例?
  • 无论类型是否完整,它都会产生影响。
  • 很简单,真的:class C; void f(C* p) { delete p; /* ~C doesn't run */}
  • 我不认为我的直觉可以作为答案。仅仅因为我想不出任何这样的案例并不意味着它们不存在。

标签: c++ include forward-declaration


【解决方案1】:

这里有两种情况。其中一个是 UB,另一个我认为是定义的行为改变(假设没有 ODR 或类似的违规行为:即,没有调用 foo 看到 A 的定义,但我不确定)

namespace N {
  struct B {};
  struct A;//:B{};
}

void foo(N::B*){
  std::cout << "B\n";
}
template<class T, class=std::enable_if_t<!std::is_convertible<T*,N::B*>{}>>
void foo(T*){
  std::cout << "T\n";
}

int main() {
  foo( (N::A*)0 );
}

struct A; 替换为struct A:B{}; 将改变调用foo 的哪个重载。

此外,如果delete A; 在调用delete A; 时可见,则delete A; 将调用~A()。否则,如果有一个非平凡的析构函数,我们就有 UB。在这种情况下,代码的含义发生了变化,因为它从 UB 转到了 DB,我想这就是含义的变化。

【讨论】:

    【解决方案2】:

    我知道的最隐蔽的例子之一是 C 风格的强制转换与继承结合。

    假设你有:

    class Parent1 {};
    class Parent2 {};
    
    class Child : public Parent1, public Parent2 {};
    

    然后在其他文件中你从 Parent2 转换为子:

    Parent2* parent2_ptr = new Child;
    Child* obj = (Child*)parent2_ptr;
    

    在完整的定义下,C 风格的演员表是static_cast,正确地修正了地址。通过(Child)的前向声明,C 风格的演员变成了reinterpret_cast,默默地破坏了代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多