类型擦除是否有效取决于优化级别

Type erasure works or fails depending on optimization level

本文关键字:优化 取决于 有效 擦除 是否 类型      更新时间:2023-10-16

我正在尝试通过类型擦除(作为简单格式化文本输出库的一部分(将值类型包装在包装器中。下面print的函数应该接受一个包装在类型擦除包装结构中的参数,该结构知道(通过函数指针(如何将其转换为字符串并打印它。

当我编译它时,它打印 0(有时是垃圾(:

g++ -std=c++11 -Wall -Wextra -O0 -o test test.cpp

但是当使用 -O1-O3 进行编译时,它会按预期工作(-Og也会失败(。使用 clang++,它的行为方式相反(启用优化时会失败(。我还尝试了 g++ -m32(我的 x86_64 Linux Mint 盒子上有 multilib gcc(和 32 位和 64 位 mingw-w64 交叉编译器。行为类似。此外,clang++-libc++似乎总是失败。

我必须触发一些未定义的行为(因为我发现 g++ 和 clang++ 不太可能有相同的错误(。这是怎么回事,我错过了什么?

#include <iostream>
#include <string>
using namespace std;
// Struct holding a pointer to a type-erased value and a function pointer to
// convert it to a string
struct TypeErasingWrapper {
    void* item;                 // Pointer to type erased value
    string (*to_string)(void*); // Function pointer to convert it to a string
};
// Convert any value pointer to a string (using the proper overload of
// std::to_string
template <typename T>
string toString (void* item)
{
    return to_string(*reinterpret_cast<T*>(item));
}
// Wrap any value in a type-erasing wrapper
template <typename T>
TypeErasingWrapper wrap(T value) {
    return {&value, toString<T>};
}
// Print a type erased value
void print(TypeErasingWrapper wrapper)
{
    cout << wrapper.to_string(wrapper.item) << endl;
}
int main() 
{
    print(wrap(1234));
}

这是没有模板的版本,其行为方式相同。

#include <iostream>
#include <string>
using namespace std;
// Struct holding a pointer to a type-erased int and a function pointer to
// convert it to a string
struct TypeErasingWrapper {
    void* item;                 // Pointer to type erased int
    string (*to_string)(void*); // Function pointer to convert it to a string
};
// Convert type-erased int to a string
string toString (void* item)
{
    return to_string(*reinterpret_cast<int*>(item));
}
// Wrap an int in a type-erasing wrapper
TypeErasingWrapper wrap(int value) {
    return {&value, toString};
}
// Print a type erased value
void print(TypeErasingWrapper wrapper)
{
    cout << wrapper.to_string(wrapper.item) << endl;
}
int main() 
{
    print(wrap(1234));
}
template <typename T>
TypeErasingWrapper wrap(T value) {
  return {&value, toString<T>};
}

您按价值获取value。 然后,将指向它的指针传递给返回值。

value仅持续到函数体结束,此时指针变为悬空指针。

更改TypeErasingWrapper以存储void const*。 更改wrap以采取const&Ttemplate<class T> std::string toString( void const * )也是。 修复剩余的生成错误。 将reinterpret_cast更改为static_cast

通常,类型擦除代码还会擦除所有权(销毁、移动,有时是复制(,以便处理生存期问题。 如果你不这样做,我建议调用你的类型blah_view,以向最终用户表明它不是真正的值类型。

作为最后一条注释,请在文件范围内停止using namespace std;。 简洁很少值得。