2015-07-01 133 views
3

我有下面的代碼,是爲了接受一個Qt的QVariant並申請函子,如果變體包含一個值:推導模板參數

template<typename T, typename Functor> 
inline QVariant map(const QVariant& v, Functor f) 
{ 
    return v.isValid() 
      ? QVariant{f(qvariant_cast<T>(v))} 
      : v; 
} 

我的問題是,編譯器不能推斷出

error: no matching function for call to `map(QVariant, <lambda(const QByteArray&)>)` 
note: candidate is: 
    template<class T, class Functor> QVariant map(const QVariant&, Functor). 
note: template argument deduction/substitution failed: 
     couldn't deduce template parameter 'T' 
:當調用該函數作爲

map(someFuncReturningQVariant(), [](const QByteArray& array) -> QString { 
    return array.toString(); 
}); 

編譯器會抱怨的T型(從原來的誤差,其具有較長的類型名稱清理)

這是因爲QVariant會在運行時擦除它包含的對象的類型。 (它仍然在內部知道它,很像boost::any,而qvariant_cast<T>()獲取原始對象)。

如何捕獲傳遞給Functor的變量的類型並在其他地方使用它?另外,如何指定Functor需要T類型的參數? (我懷疑這實際上是同一個問題,或者說他們至少有相同的答案。)

請注意,我的辦法正常工作與std::optional,因爲類型也不會被刪除:

using std::experimental::optional; 

template<typename T, typename Functor> 
inline auto map(const optional<T>& v, Functor f) -> optional<decltype(f(*v))> 
{ 
    return v ? optional<decltype(f(*v))>{f(*v)} 
      : v; 
} 

還要注意如果我手動指定類型,則QVariant代碼可以正常工作:

map<QByteArray>(someFuncReturningQVariant(), [](const QByteArray& array) -> QString { 
    return array.toString(); 
}); 

但是,當然,這很醜陋。

+0

你有C++ 14嗎? – Yakk

+0

@Yakk,如果我能用C++ 11脫身,我會用它,但在學術上,是的。 – thirtythreeforty

回答

3

基本上你想要的是:給定一個仿函數,F,確定其第一個參數的衰變類型。

爲此,我們需要function_traits,有了它我們可以這樣做:

template <typename F> 
using first_arg = std::decay_t<typename function_traits<F>::template arg<0>::type>; 

我們使用:

template<typename Functor> 
inline QVariant map(const QVariant& v, Functor f) 
{ 
    using T = first_arg<Functor>; 

    return v.isValid() 
      ? QVariant{f(qvariant_cast<T>(v))} 
      : v; 
} 
+0

完美!這是一個神祕的「使用」聲明;我希望C++有一些像Rust的where子句。好吧。是否有一個規範的'function_traits'或者它只是人們寫的東西,因爲它很有用? – thirtythreeforty

+1

@Barry function_traits是一個不錯的工具。我以爲你可能有興趣知道它不能用於std :: bind(f,std :: placeholders :: _ 1) –

+0

@RichardHodges你不能推斷函數模板的參數類型,對吧?你必須提供它。 – Barry

2
map<QByteArray>(someFuncReturningQVariant(), [](auto&& array){ 
    return array.toString(); 
}); 

是C++ 14的方式來做到這一點:不要在lambda中指定類型,而應該僅指定爲模板參數。

還要注意->QString處於冗餘C++ 11或14

或者,知道QVariant不是一個類型ameniable到映射。明確地在你的地圖功能外部進行轉換。

地圖QVariantoptional<T&>外部(或T*),並返回optional<std::result_of_t<F(T&)>>(或QVariant如果你喜歡扔掉信息)。

+0

哎呀。我早先偶然發現並忘記了它。 Lambda類型演繹非常酷。最後,我必須返回QVariant,因爲我正在實現一個Qt代理模型。 – thirtythreeforty