通过将前向声明替换为 include 来更改代码的含义
Change the meaning of code by replacement of forward declaration with include
In extreme cases, replacing an #include with a forward declaration can silently change the meaning of code.
你能帮我找一些例子吗?
这里有两种情况。其中之一是 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,我想这是一个含义的变化。
我所知道的最阴险的例子之一是 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
无声地破坏代码。
In extreme cases, replacing an #include with a forward declaration can silently change the meaning of code.
你能帮我找一些例子吗?
这里有两种情况。其中之一是 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,我想这是一个含义的变化。
我所知道的最阴险的例子之一是 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
无声地破坏代码。