类型擦除工作或取决于优化级别失败
问题描述:
我想通过类型擦除(作为简单的格式化文本输出库的一部分)在包装中包装值类型。下面的函数print
应该采用封装在类型擦除包装器结构中的参数,该结构知道(通过函数指针)如何将其转换为字符串并进行打印。类型擦除工作或取决于优化级别失败
它打印出0(或有时垃圾)当我编译它:
g++ -std=c++11 -Wall -Wextra -O0 -o test test.cpp
但它工作时通过-O3
(-Og
也失败)与-O1
编译如预期。使用clang ++时,它的行为相反(启用优化时失败)。我也试过g ++ -m32(我在我的x86_64 Linux Mint盒上有multilib gcc)和32位和64位mingw-w64交叉编译器。行为是相似的。此外,铿锵++ - 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&T
。还有template<class T> std::string toString(void const *)
。修复剩余的构建错误。在此期间,将reinterpret_cast
更改为static_cast
。
通常,类型擦除代码还会擦除所有权(破坏,移动,有时是复制)以处理生命期问题。如果你不这样做,我建议你打电话给你的类型blah_view
,以使最终用户清楚它并不是真正的价值型。
作为最终评论,请在文件范围停止using namespace std;
。简洁是很不值得的。
嘎!不能相信我做到了。再次。谢谢你,先生!此外,我通常不会在文件范围使用名称空间标准(我想在此提供一个简洁的版本),但感谢提示! – cyco130 2015-02-23 19:40:59