2015-02-23 48 views
1

我想通过类型擦除(作为简单的格式化文本输出库的一部分)在包装中包装值类型。下面的函数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)); 
} 

回答

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;。简洁是很不值得的。

+0

嘎!不能相信我做到了。再次。谢谢你,先生!此外,我通常不会在文件范围使用名称空间标准(我想在此提供一个简洁的版本),但感谢提示! – cyco130 2015-02-23 19:40:59