2014-09-01 72 views
4

編輯:由於幾個原因,問題中概述的方法存在問題。最後,我以不同的方式解決了這個問題,請參閱下面的答案。檢查可召回的模板參數類型

我有一些模板類,其中的模板參數預計是一個可調用匹配某個簽名。如果用戶提供的模板參數不是可調用的或者與預期的簽名不匹配,那麼編譯將在回調機制內部深陷失敗,並且產生的錯誤消息很難破譯。相反,如果給定的模板參數無效,我希望能夠使用static_assert提供一個好的,易於理解的錯誤消息。不幸的是,這似乎很難做到。

我使用以下設置:

#include <type_traits> 

namespace detail { 

template <typename Function, typename Sig> 
struct check_function 
{ 
    static constexpr bool value = 
     std::is_convertible<Function, typename std::decay<Sig>::type>::value; 
}; 

template <typename, typename> 
struct check_functor; 

template <typename Functor, typename Ret, typename... Args> 
struct check_functor<Functor, Ret(Args...)> 
{ 
    typedef Ret (Functor::*Memfn) (Args...); 

    static constexpr bool value = 
     check_function<decltype(&Functor::operator()), Memfn>::value; 
}; 

} // end namespace detail 

template <typename Func, typename Sig> 
struct check_function_signature 
{ 
    using Type = 
     typename std::conditional< 
        std::is_function<Func>::value, 
        detail::check_function<Func, Sig>, 
        detail::check_functor<Func, Sig>>::type; 

    static constexpr bool value = Type::value; 
}; 

即,如果Func是一個函數指針類型,它直接相比所需的簽名,否則其被假定爲一個算符和其operator()是相反。

這似乎是一些簡單的功能和用戶定義的函數子工作,但由於某種原因,我無法理解失敗的lambda表達式(鏗鏘3.4測試和g ++ 4.8):

int f(int i); 

struct g 
{ 
    int operator() (int i) { return i; } 
}; 

int main() 
{ 
    static_assert(check_function_signature<decltype(f), int(int)>::value, 
        "Fine"); 

    static_assert(check_function_signature<g, int(int)>::value, 
        "Also fine"); 

    auto l = [](int i) { return i; }; 
    static_assert(check_function_signature<decltype(l), int(int)>::value, 
        "Fails"); 
} 

我的理解是該標準要求lambda類實現爲等效於上面g的匿名函子,所以我不明白爲什麼前者可行,但後者不行。

因此,總的來說,我的問題:

  • 是我在這裏用實際合理的做法,或已我做一個明顯的錯誤?
  • 爲什麼這似乎適用於用戶定義的仿函數,但編譯器定義的仿函數(即lambda表達式)失敗?
  • 是否有修復/解決方法,以便可以用這種方式檢查lambda?
  • 我可以對此代碼進行其他改進嗎? (可能很多...)

在此先感謝,這是推動我的模板元編程知識的限制,所以任何建議將感激地收到。

+1

來自[此答案](http://stackoverflow.com/a/12283159/3920237)代碼似乎工作。 [現場示例](http://coliru.stacked-crooked.com/a/f951bb2b4ca90efd) – 2014-09-01 11:02:41

回答

2

(回答我的問題,我想出更好的辦法來實現我想要的東西,我想我會分享它。)

根據回覆,特別是remaybel的評論中的鏈接回答,我最終得到了大量代碼,它們從函子的operator()中剝離類類型,並根據所需的簽名檢查每個參數類型。然而,事實證明這種方法效果不好,因爲要求獲得指向T::operator()的成員指針意味着如果存在多個operator()的重載或者它被定義爲模板,則失敗。我也不確定它在所有情況下都能正確處理參數轉換,並且有很多事情很難正確地進行。

再想一想,我意識到我真正想要做的是試圖用我所需的參數類型構造一個函數調用,並且如果無法進行這樣的調用就會失敗。後來黑客的一點點,我想出了這個:

template <typename, typename, typename = void> 
struct check_signature : std::false_type {}; 

template <typename Func, typename Ret, typename... Args> 
struct check_signature<Func, Ret(Args...), 
    typename std::enable_if< 
     std::is_convertible< 
      decltype(std::declval<Func>()(std::declval<Args>()...)), 
      Ret 
     >::value, void>::type> 
    : std::true_type {}; 

這種構造使用declval兩個可調用本身和參數的「虛擬」函數調用,並檢查結果可以轉換到所需的類型。如果這樣的呼叫無效,SFINAE會進入並且部分專業化被拒絕。

這比我之前嘗試做的更短,並且(IMO)更加優雅。它也適用於我試圖拋出的每個可調用對象。儘管如此,正如我在原始問題中所說的那樣,這推動了我的元編程知識的極限,所以如果有任何關於如何改進此代碼的建議,請讓我知道。

1

您錯過了operator()中的const說明符。

有了:

template <typename Functor, typename Ret, typename... Args> 
struct check_functor<Functor, Ret(Args...)> 
{ 
    typedef Ret (Functor::*Memfn) (Args...) const; // const added here 

    static constexpr bool value = 
     check_function<decltype(&Functor::operator()), Memfn>::value; 
}; 

的檢查是正確的(非可變)拉姆達(但不包括您的自定義可變仿函數)。 否則你必須讓你的λ可變:

auto l = [](int i) mutable { return i; };