2016-01-05 63 views
1

我遇到一個錯誤,當使用模板類型演繹結合C++ 14 std ::得到<>與類型索引。代碼可能看起來有點複雜,但我試圖將其簡化爲對正在發生的事情的基本知識。它實際上只是一個Observer模式...結構'A'允許根據消息類型(M1,M2,...)設置觀察者。請注意,每個消息類型只有一個觀察者,爲了簡單起見。C++ 14元組類型索引失敗推斷類型從std :: bind我想成爲std :: function

現在,技巧(以及失敗的部分)正在使用C++ 14的std :: get <>,它允許您使用實際類型索引到唯一類型的元組。下面是一個簡單的例子證明了我的意思:

void sample() 
{ 
    std::tuple<int, float> myTuple; 
    std::get<float>(myTuple) = 3.141f; // C++14 allows this 
    std::get<1>(myTuple) = 3.141f;  // C++11 way to do it, using index 
} 

考慮到這一點,這是我的節目(從上面的代碼分開)不編譯,因爲C++ 14元組類型的索引上的失敗推斷類型:

#include <cxxabi.h> 
#include <stdlib.h> 
#include <functional> 
#include <vector> 
#include <tuple> 
#include <typeinfo> 
#include <iostream> 
#include <string> 

// =================================== 
// A quick'n'dirty way to print types (nonportable) 
// And yes, I know this code could be improved :) 
inline 
std::string demangle(char const *mangled) 
{ 
    char *output = (char *)malloc(16384); 
    size_t length = 16384; 
    int status; 

    __cxxabiv1::__cxa_demangle(mangled, output, &length, &status); 
    std::string s(output, length); 
    free(output); 
    return s; 
} 

#define DEMANGLE(T) demangle(typeid(T).name()) 

// =================================== 
struct A 
{ 
    struct M1 
    {}; 

    struct M2 
    {}; 

    using Tuple = std::tuple< 
     std::function<void(M1 const &)> 
     ,std::function<void(M2 const &)> 
    >; 

    template<typename T> 
    void setObserver(T func) 
    { 
     // This works fine 
     std::cout << DEMANGLE(T) << std::endl; 

     // ************************************************ 
     // The line below does not compile (std::get fails) 
     // 
     // Note the type of T prints out as: 
     // std::_Bind<std::_Mem_fn<void (B::*)(A::M1 const&)> (B*, std::_Placeholder<1>)> 
     // 
     // Rather than the (desired): 
     // std::function<void (A::M1 const&)>(A::M1 const&)> (B*, std::_Placeholder<1>)> 
     // 
     // ************************************************ 
     std::get<T>(tuple_) = func; // C++14 only 
    } 

private: 
    Tuple tuple_; 
}; 

// =================================== 
struct B 
{ 
    void func(A::M1 const &) 
    {} 
}; 

// =================================== 
int main() 
{ 
    A *a = new A; 
    B *b = new B; 

    using namespace std::placeholders; 

    a->addObserver(std::bind(&B::func, b, _1)); 

    return 0; 
} 

UPDATE:

提出的解決方案不能解決問題,從性病::綁定(...)轉換爲標準::函數(...)的問題,但它需要我爲我的每個類型M1,M2,...,

0123都有一個單獨的setObserver()函數

我該如何模板化setObserver()來解決這個問題?

回答

3

std::bind()不返回std::function但某些未指定類型可轉換爲std::function<>。其次,C++ 14表格中的std::get<>()需要精確類型,不能轉換爲元組成員類型之一。

爲了實現你想要的 - 你需要convert你的T到你的元組類型之一。

E.g.你可能你目前setObserver移動到私有部分,並重新命名它 - 創建功能所需類型:

template<typename T> 
    auto setObserver(T func) 
     -> typename std::enable_if<std::is_convertible<T, std::function<void(M1 const &)>>::value>::type 
    { 
     this->template setObserverImpl<std::function<void(M1 const &)>>(func); 
    } 
    template<typename T> 
    auto setObserver(T func) 
     -> typename std::enable_if<std::is_convertible<T, std::function<void(M2 const &)>>::value>::type 
    { 
     this->template setObserverImpl<std::function<void(M2 const &)>>(func); 
    } 

private: 
    template<typename T> 
    void setObserverImpl(T func) 
    { 
    // no change here 
    } 

你需要讓當前setObserver爲模板功能,除非你只有兩種類型。

到Template這個解決方案 - 無論是使用變形功能,用於每個Mx類型,其中「非敞篷版」將是空的:

template <typename T, typename M> 
    auto setObserverTemplate(T func) -> typename std::enable_if<std::is_convertible<T, std::function<void(M const &)>>::value>::type 
    { 
     this->template setObserverImpl<std::function<void(M const &)>>(func); 
    } 
    template <typename T, typename M> 
    auto setObserverTemplate(T) -> typename std::enable_if<not std::is_convertible<T, std::function<void(M const &)>>::value>::type 
    { 
     // do nothing 
    } 

    template<typename T> 
    auto setObserver(T func) 
    { 
     this->template setObserverTemplate<T,M1>(func); 
     this->template setObserverTemplate<T,M2>(func); 
    } 

或者,可能要好得多 - 可變參數版本與定點empty功能:

template<typename T, typename MFirst, typename ...M> 
    auto setObserverVariadic(T func) 
    { 
     this->template setObserverTemplate<T,MFirst>(func); 
     this->template setObserverVariadic<T,M...>(func); 
    } 
    template<typename T> 
    auto setObserverVariadic(T) 
    { 
    } 

    template<typename T> 
    auto setObserver(T func) 
    { 
     this->template setObserverVariadic<T,M1,M2>(func); 
    } 

最後一條評論 - 您可能會嘗試從您的元組類型中「檢索」當前類型(我的意思是這些Mx類型的列表)。怎麼做 - 這可能是新問題的好選擇。

+0

我試圖避免每個M1,M2,都有一個單獨的setObserver()函數......這就是爲什麼我模板化的東西。有沒有辦法只有一個setObsrver()模板函數,而不是一個用於M1和一個用於M2? – user5406764

+0

@ user5406764 - 查看我的更新 – PiotrNycz

相關問題