2015-02-23 20 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