类型擦除工作或失败取决于优化级别
Type erasure works or fails depending on optimization level
我正在尝试通过类型擦除将值类型包装在包装器中(作为简单格式化文本输出库的一部分)。下面的函数 print
应该采用包装在类型擦除包装器结构中的参数,该结构知道(通过函数指针)如何将其转换为字符串并打印出来。
当我编译它时它打印 0(或者有时是垃圾):
g++ -std=c++11 -Wall -Wextra -O0 -o test test.cpp
但是当使用 -O1
到 -O3
编译时它按预期工作(-Og
也失败)。使用 clang++,它的行为正好相反(启用优化时它会失败)。我还尝试了 g++ -m32(我的 x86_64 Linux Mint box 上有 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
。然后将指向它的指针传递给 return 值。
值value
只持续到函数体结束,此时指针变成悬空指针。
更改 TypeErasingWrapper
以存储 void const*
。将 wrap
更改为 const&T
。 template<class T> std::string toString( void const * )
还有。修复剩余的构建错误。将 reinterpret_cast
更改为 static_cast
。
典型的类型擦除代码也会擦除所有权(销毁、移动,有时是复制)以处理生命周期问题。如果你不这样做,我建议你调用你的类型 blah_view
来让最终用户清楚它不是真正的值类型。
作为最后的评论,在文件范围内停止 using namespace std;
。简洁是不值得的。
我正在尝试通过类型擦除将值类型包装在包装器中(作为简单格式化文本输出库的一部分)。下面的函数 print
应该采用包装在类型擦除包装器结构中的参数,该结构知道(通过函数指针)如何将其转换为字符串并打印出来。
当我编译它时它打印 0(或者有时是垃圾):
g++ -std=c++11 -Wall -Wextra -O0 -o test test.cpp
但是当使用 -O1
到 -O3
编译时它按预期工作(-Og
也失败)。使用 clang++,它的行为正好相反(启用优化时它会失败)。我还尝试了 g++ -m32(我的 x86_64 Linux Mint box 上有 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
。然后将指向它的指针传递给 return 值。
值value
只持续到函数体结束,此时指针变成悬空指针。
更改 TypeErasingWrapper
以存储 void const*
。将 wrap
更改为 const&T
。 template<class T> std::string toString( void const * )
还有。修复剩余的构建错误。将 reinterpret_cast
更改为 static_cast
。
典型的类型擦除代码也会擦除所有权(销毁、移动,有时是复制)以处理生命周期问题。如果你不这样做,我建议你调用你的类型 blah_view
来让最终用户清楚它不是真正的值类型。
作为最后的评论,在文件范围内停止 using namespace std;
。简洁是不值得的。