[英]Crash due to mixing CRTP & interfaces?
I am experimenting with CRTP and mixing it with interfaces, and I cannot explain why this program crashes (in Clang, GCC and MSVC).我正在试验 CRTP 并将其与接口混合,但我无法解释为什么该程序会崩溃(在 Clang、GCC 和 MSVC 中)。 In latest Clang and GCC it builds without warning with -Wall, -Wextra.在最新的 Clang 和 GCC 中,它在没有警告的情况下使用 -Wall、-Wextra 构建。 My guess is that the virtual method call is not resolved but I cannot explain why (no crash if GetInt() is removed from the interface).我的猜测是虚拟方法调用没有解决,但我无法解释原因(如果从接口中删除 GetInt() 不会崩溃)。 In my debugger I see that the crash happens on line static_cast<T*>(this)->GetInt()
.在我的调试器中,我看到崩溃发生在static_cast<T*>(this)->GetInt()
。
#include <iostream>
class INode
{
public:
virtual int GetInt() = 0;
protected:
~INode() {}
};
template<template<class> typename NodeBase>
class Node : public INode, public NodeBase<Node<NodeBase>>
{
public:
int GetInt() override { return 42; }
};
template<typename T>
class MyNodeBase
{
public:
int CallGetInt() {
return static_cast<T*>(this)->GetInt();
}
};
template<template<class> typename NodeBase>
int Foo1(Node<NodeBase> n) {
return n.CallGetInt();
}
template<typename T>
int Foo2(MyNodeBase<T> n) {
return n.CallGetInt();
}
int main() {
Node<MyNodeBase> n;
std::cout << Foo1(n) << std::endl; // ok
std::cout << Foo2(n) << std::endl; // crash
}
You're slicing n
in your call to Foo2
.您在对Foo2
的调用中对n
进行切片。
Foo2
accepts its parameter by value. Foo2
按值接受其参数。 That means that a copy of n
's MyNodeBase<Node<MyNodeBase>>
sub-object is what gets passed to Foo2
.这意味着n
的MyNodeBase<Node<MyNodeBase>>
子对象的副本是传递给Foo2
。 Since n
in Foo2
is not a Node<MyNodeBase>
, calling GetInt
through the pointer returned from the cast in CallGetInt
results in your program exhibiting undefined behavior.由于Foo2
中的n
不是Node<MyNodeBase>
,因此通过从CallGetInt
中的转换返回的指针调用GetInt
会导致您的程序表现出未定义的行为。
If you change Foo2
to accept its parameter by reference, you program's behavior would be well-defined.如果您更改Foo2
以通过引用接受其参数,则您的程序的行为将是明确定义的。
Foo2
argument is passed by value. Foo2
参数按值传递。 So the complete object n
is of type MyNodeBase<Node<MyNodeBase>>
.所以完整的 object n
是MyNodeBase<Node<MyNodeBase>>
类型。 It is not a derived of Node<MyNodeBase>
.它不是Node<MyNodeBase>
的派生。 But the expression static_cast<T*>(this)
assumes that this
is derived of Node<MyNodeBase>
thus static_cast<T*>(this)->GetInt()
is undefined behavior.但是表达式static_cast<T*>(this)
假定this
是从Node<MyNodeBase>
派生的,因此static_cast<T*>(this)->GetInt()
是未定义的行为。
You avoid this error:您可以避免此错误:
template<typename T>
class MyNodeBase
{
protected:
MyNodeBase() = default;
MyNodeBase(const MyNodeBase&) = default;
MyNodeBase& operator = (const MyNodeBase&) = default;
public:
int CallGetInt() {
return static_cast<T*>(this)->GetInt();
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.