在类型擦除的小对象优化中调试崩溃
Debugging crash in small object optimization for type erasure
我正在实现一个 class 来对小对象执行类型擦除,但遇到了一个我不理解的分段错误。
以下程序:
#include <iostream>
#include <type_traits>
struct small_object
{
public:
template<class T>
small_object(const T& value)
{
new(&storage_) concrete<T>(value);
}
~small_object()
{
get_abstract().~abstract();
}
void print() const
{
// XXX crash here
get_abstract().print();
}
private:
struct abstract
{
virtual ~abstract(){}
virtual void print() const = 0;
};
template<class T>
struct concrete
{
concrete(const T& value) : value_(value) {}
void print() const
{
std::cout << value_ << std::endl;
}
T value_;
};
abstract& get_abstract()
{
return *reinterpret_cast<abstract*>(&storage_);
}
const abstract& get_abstract() const
{
return *reinterpret_cast<const abstract*>(&storage_);
}
typename std::aligned_storage<4 * sizeof(void*)> storage_;
};
int main()
{
small_object object(13);
// XXX i expect this line to print '13' to the terminal but it crashes
object.print();
return 0;
}
在 XXX
指示的行崩溃。
我认为问题是对 .print()
的虚拟调用没有被正确地动态调度,但我不明白为什么。
谁能告诉我我错过了什么?
您没有从 abstract
派生 concrete<T>
,因此当您使用放置 new
构造对象时没有创建 vtable。因此,当您尝试调用虚函数时,它会失败; concrete<T>
和 abstract
在这个例子中实际上是完全不相关的类型。
如果您使用的是 C++11 或更高版本,我建议您使用 override
关键字,以允许编译器在这种情况下生成错误。
std::aligned_storage<4 * sizeof(void*)> storage_;
这将创建一个字节的存储空间。
模板参数不设置声明对象的大小,而是设置可以分配到该类型大小合适的数组中的对象的大小。因此,你需要
std::aligned_storage<4 * sizeof(void*)> storage_[4 * sizeof(void*)];
GCC 6.2.0 警告您:
warning: placement new
constructing an object of type ‘small_object::concrete<int>
’ and size ‘16
’ in a region of type ‘std::aligned_storage<32ul>
’ and size ‘1
’ [-Wplacement-new=]
(您仍然需要从 abstract
导出 concrete
)。
我正在实现一个 class 来对小对象执行类型擦除,但遇到了一个我不理解的分段错误。
以下程序:
#include <iostream>
#include <type_traits>
struct small_object
{
public:
template<class T>
small_object(const T& value)
{
new(&storage_) concrete<T>(value);
}
~small_object()
{
get_abstract().~abstract();
}
void print() const
{
// XXX crash here
get_abstract().print();
}
private:
struct abstract
{
virtual ~abstract(){}
virtual void print() const = 0;
};
template<class T>
struct concrete
{
concrete(const T& value) : value_(value) {}
void print() const
{
std::cout << value_ << std::endl;
}
T value_;
};
abstract& get_abstract()
{
return *reinterpret_cast<abstract*>(&storage_);
}
const abstract& get_abstract() const
{
return *reinterpret_cast<const abstract*>(&storage_);
}
typename std::aligned_storage<4 * sizeof(void*)> storage_;
};
int main()
{
small_object object(13);
// XXX i expect this line to print '13' to the terminal but it crashes
object.print();
return 0;
}
在 XXX
指示的行崩溃。
我认为问题是对 .print()
的虚拟调用没有被正确地动态调度,但我不明白为什么。
谁能告诉我我错过了什么?
您没有从 abstract
派生 concrete<T>
,因此当您使用放置 new
构造对象时没有创建 vtable。因此,当您尝试调用虚函数时,它会失败; concrete<T>
和 abstract
在这个例子中实际上是完全不相关的类型。
如果您使用的是 C++11 或更高版本,我建议您使用 override
关键字,以允许编译器在这种情况下生成错误。
std::aligned_storage<4 * sizeof(void*)> storage_;
这将创建一个字节的存储空间。
模板参数不设置声明对象的大小,而是设置可以分配到该类型大小合适的数组中的对象的大小。因此,你需要
std::aligned_storage<4 * sizeof(void*)> storage_[4 * sizeof(void*)];
GCC 6.2.0 警告您:
warning:
placement new
constructing an object of type ‘small_object::concrete<int>
’ and size ‘16
’ in a region of type ‘std::aligned_storage<32ul>
’ and size ‘1
’ [-Wplacement-new=]
(您仍然需要从 abstract
导出 concrete
)。