2015-06-12 65 views
0

是否可以提取區分聯合的類型來初始化一個「自動」變量?很簡單,你將類型傳遞給模板,但我想要一些「自動」的東西。使用訪問者函數或使用有界列表類型(如mpl :: vector)的解決方案會很好。是否有可能自動從歧視聯盟中提取類型?

一個示例如下所示:

#include <iostream> 
#include <typeindex> 
#include <cassert> 

struct d_union { 
    template <typename T> 
    d_union(T t) { 
     *reinterpret_cast<T*>(data) = t; 
     _type_id = &typeid(T); 
    } 

    template <typename T> 
    const T* get_pointer() const { 
     if (_type_id == &typeid(T)) 
      return reinterpret_cast<const T*>(data); 
     else 
      return nullptr; 
    } 

    template <typename T> 
    const T get() const { 
     assert (_type_id == &typeid(T)); 
     return *get_pointer<T>(); 
    } 

    alignas(8) char data[8]; 
    const std::type_info *_type_id; 
}; 


std::ostream& operator<<(std::ostream&os, const d_union &u) { 
    if (auto ip = u.get_pointer<int>()) 
     os << *ip; 
    if (auto fp = u.get_pointer<float>()) 
     os << *fp; 
    return os; 
} 

int main() { 

    d_union _i = d_union(42); 
    d_union _f = d_union(3.14f); 

    std::cout << "d_union(42) = " << _i << std::endl; 
    std::cout << "d_union(3.14) = " << _f << std::endl; 

    int _get_i = _i.get<int>(); 
    std::cout << "d_union(42).get<int>() = " << _get_i << std::endl; 

    // auto _get_auto = _i.get(); 
    // std::cout << "d_union(42).get()" << _get_auto << std::endl; 

} 

在任何可能的解決方案,將不勝感激!

感謝

+0

在C++中沒有辦法做到這一點。您在get操作中將內存區域的類型指定爲特定類型,而無法從類中知道類型中實際存儲的內容。 auto如何「知道」該類型正在返回?類型的模板「列表」可能是這些庫使用的內容,但這仍然涉及從列表中選擇您想要返回的類型,即列表會讓你獲得char並返回它。真的沒什麼改進。 – ceorron

+1

此代碼違反了嚴格的別名規則('char'可能不會被別名爲'T');解決這個問題'alignas(8)char data [8];''aligned_storage <8,8> data;' –

回答

0

通常,當您覆蓋全局流運營商的自定義類的類型,你應該實現它們調用成員方法而言,讓類決定如何流本身,如:

struct d_union { 
    ... 
    void outputTo(std::ostream &os) const { 
     if (auto ip = get_pointer<int>()) 
      os << *ip; 
     else if (auto fp = get_pointer<float>()) 
      os << *fp; 
     ... 
    } 
    ... 
}; 

std::ostream& operator<<(std::ostream &os, const d_union &u) { 
    u.outputTo(os); 
    return os; 
} 

話雖這麼說,如果你想要的東西更「自動」時被調用operator<<,也許你可以嘗試這樣的事情(不完美,但接近你可能會尋找考慮d_union不知道它在運行時持有什麼,如果你加入operator=那麼它甚至可以動態改變類型):

typedef void (*pOutputProc)(const d_union&, std::ostream&); 

template <typename T> 
void outputProc(const d_union &u, std::ostream &os) 
{ 
    os << *(u.get_pointer<T>()); 
} 

std::unordered_map<std::type_index, pOutputProc> outputProcs; 

template <typename T> 
void registerOutputProc() 
{ 
    outputProcs[std::type_index(typeid(T))] = &outputProc<T>; 
} 

void registerOutputProcs() 
{ 
    registerOutputProc<int>(); 
    registerOutputProc<float>(); 
    ... 
} 
#pragma startup registerOutputProcs 

struct d_union { 
    ... 
    void outputTo(std::ostream &os) const { 
     pOutputProc proc = outputProcs[std::type_index(*_type_id)]; 
     if (proc) proc(*this, os); 
    } 
    ... 
}; 

std::ostream& operator<<(std::ostream &os, const d_union &u) { 
    u.outputTo(os); 
    return os; 
} 

那麼你就只需要填充registerOutputProcs()與您要d_union以支持不同的數據類型。

+0

這是非常糟糕的代碼。 – Puppy

+0

它來自我在[std :: type_index'文檔](http://en.cppreference.com/w/cpp/types/type_index)中找到的示例。我承認這並不完美,但它應該是有用的。與KentH'差不多可以考慮'd_union'的數據類型直到運行時才知道。 –

+0

實際上並非如此。 – Puppy

0

一般來說,這是不可能的,因爲C++不是一種動態類型的語言。

在特定情況下,(您的情況)operator<<()需要檢查type_info並根據實際存在的類型進行操作。其侷限性在於,有必要對可以存儲在struct中的類型進行硬編碼知識 - 如果您想添加對不同類型的支持,則需要添加代碼並重新構建程序。

auto依賴編譯器在編譯時知道類型。 type_info在運行時獲取值。

1

您正在查找Boost.TypeErasure圖書館。這會讓你以自然的方式流出任何流。從教程:

any< 
    mpl::vector< 
     copy_constructible<>, 
     typeid_<>, 
     incrementable<>, 
     ostreamable<> 
    > 
> x(10); 
++x; 
std::cout << x << std::endl; // prints 11 

x這裏能夠滿足給定的概念,任何類型的。

如果這不是你想要的,那麼Boost也有一個名爲Variant的歧視聯盟庫,它有一個訪問者接口。