2016-06-28 16 views
2

讓我們說我有這樣一個泛型函數:C++驗證模板類型的可調用簽名

template<typename TFunc> 
void templFunc(TFunc func) { 
    func(3, 6); 
} 

有什麼辦法在編譯的時候,我可以驗證TFunc的簽名,無論它是一個標準: :函數或任何類型的lambda或函數引用。我只是想確保TFunc的簽名爲void(int,int)或類似的static_assert,所以我可以產生一個非垃圾的錯誤消息。

+0

如果拉姆達是不要求那麼您可能使用'static_assert(標準:: is_same ::值, 「錯誤」);' – iammilind

回答

0

您可以使用:

template<typename TFunc> 
void templFunc(TFunc func) { 
    static_assert(std::is_void<decltype(func(0,0))>::value, 
       "Bad template argument. The return type is not void"); 
    func(3, 6); 
} 
  1. 這將確保該函數的返回類型是void
  2. 如果函數不帶兩個參數,它將在調用func(3,6);中失敗 - 兩次。一旦進入static_assert行,並在下一行中進行一次。

  3. 如果參數類型爲intint可以升級,轉換爲或轉換爲其他類型,則函數調用將成功。確保參數類型僅爲int將需要一些額外的工作。我不確定這是什麼意思。

測試程序:

#include <type_traits> 

template<typename TFunc> 
void templFunc(TFunc func) { 
    static_assert(std::is_void<decltype(func(0,0))>::value, "Bad template argument. The return type is not void"); 
    func(3, 6); 
} 

void foo() 
{ 
} 

void bar(int x, int y) 
{ 
} 

int baz(int x, int y) 
{ 
    return 0; 
} 

struct Functor 
{ 
    void operator()(long, long) 
    { 
    } 
}; 

int main() 
{ 
    templFunc(foo); // Not OK. Two few arguments 
    templFunc(bar); // OK 
    templFunc(baz); // Not OK. Wrong return type 
    templFunc([](int, int) -> void {}); // OK 
    templFunc(Functor()); // OK 
} 
0

事情是這樣的作品使用SFINAE(僅當你做出斷言基於模板參數,不知道是什麼原因,我認爲這是最有趣的部分:)):

#include <type_traits> 

template<typename TFunc> 
typename std::enable_if<std::is_same<typename std::result_of<TFunc(int, int)>::type, void>::value >::type templFunc(TFunc func) 
{ 
    func(3, 6); 
} 

template<typename TFunc> 
typename std::enable_if<!std::is_same<typename std::result_of<TFunc(int, int)>::type, void>::value >::type templFunc(TFunc func) 
{ 
    static_assert(std::is_same<typename std::result_of<TFunc(int, int)>::type, void>::value, "error; invalid function"); 
} 

auto a = [](int, int) {}; 
auto b = [](int x, int y) { return x + y; }; 

int main() 
{ 
    templFunc(b); 
    return 0; 
} 
+0

根據標準'dcl.dcl',「_在一個static_assert聲明中,常量表達式應該是一個常量表達式(5.19),可以是 **上下文**轉換爲bool ..._」...所以我想當模板依賴關係是**不包括在'static_assert'表達式中,那麼它可以在函數模板的上下文之外進行評估,這樣它就不依賴於函數模板是否被重載解析評估?它總是被評估? – mkal

+0

對於定義爲auto a = [](long,int){}的lambda函數返回'true';' – user2296177

+0

@ user2296177:是的,還有'double'或其他可以隱式轉換爲'int'的東西。當返回類型不正確時,它也只給出友好的錯誤消息,而不是當參數的數量或類型錯誤時... – mkal

1

所以我與一些type_traits擺弄東西過來,我覺得我有一些驗證整個簽名,而不僅僅是返回值,並允許您CRE當簽名不匹配時,易於閱讀static_asserts而不是難以辨認的模板錯誤。這是一個不好的解決方案?

#include <functional> 

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

template<typename TFunc, typename Ret, typename... Args> 
struct is_signature<TFunc, Ret(Args...), 
     typename std::enable_if< 
      std::is_convertible< 
       TFunc, 
       std::function<Ret(Args...)> 
      >::value 
     >::type 
    > : public std::true_type 
    {}; 

// works on both functions and lambda's 
void blah(int, int) { 
} 

template<typename TFunc> 
void templFunc(TFunc func) { 
    static_assert(is_signature<TFunc, void(int, int)>::value, "Not gonna work! more info follows:"); 
    func(3, 6); 
} 

int main() { 
    auto b = [](int, int) -> void { 
    }; 

    auto c = [](int) -> void { 
    }; 

    static_assert(is_signature<decltype(b), void(int, int)>::value, "b convertible to a std::function<void(int, int), so this checks out!"); 
    static_assert(is_signature<decltype(b), void(int)>::value, "b not convertible to a std::function<void(int)>, so this will error in compilation."); 
    static_assert(is_signature<decltype(blah), void(int, int)>::value, "blah convertible to a std::function<void(int, int), so this checks out!"); 
    static_assert(is_signature<decltype(blah), void(int)>::value, "blah not convertible to a std::function<void(int)>, so this will error in compilation."); 

    templFunc(b); // <- ok 
    templFunc(c); // <- static assertion : not gonna work! 
    return 0; 
} 
+0

你確定這個工作正常嗎? 'auto_a = [](int,int){return 0;'is_signature :: value == true' };' – user2296177

+0

也不會給'auto a = [](int,char){};' – mkal

+0

@ user2296177和@mkal錯誤 - 你是對的。如何將'auto b = [](int,int){return 0;}'分配給'std :: function '。當我寫'std :: function meh = b;'?我沒有得到任何的錯誤,但這些類型是如何轉換的? – FatalCatharsis