我试图通过类型擦除(作为简单格式化文本输出库的一部分)将值类型包装在包装器中。下面的函数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 ++不太可能有相同的bug)。发生了什么,我错过了什么?
#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));
}
答案 0 :(得分:3)
template <typename T>
TypeErasingWrapper wrap(T value) {
return {&value, toString<T>};
}
您按value
按值。然后,将指针传递给返回值。
值value
仅持续到函数体结束,此时指针变为悬空指针。
更改TypeErasingWrapper
以存储void const*
。更改wrap
以获取const&T
。 template<class T> std::string toString( void const * )
也是如此。修复剩余的构建错误。在你的时候将reinterpret_cast
更改为static_cast
。
通常,类型擦除代码也会删除所有权(销毁,移动,有时复制)以处理生命周期问题。如果您不这样做,我建议您调用类型blah_view
,以便向最终用户说明它不是真正的值类型。
作为最终评论,请在文件范围内停止using namespace std;
。简洁很少值得。