讓我們說我有這樣一個泛型函數:C++驗證模板類型的可調用簽名
template<typename TFunc>
void templFunc(TFunc func) {
func(3, 6);
}
有什麼辦法在編譯的時候,我可以驗證TFunc的簽名,無論它是一個標準: :函數或任何類型的lambda或函數引用。我只是想確保TFunc的簽名爲void(int,int)或類似的static_assert,所以我可以產生一個非垃圾的錯誤消息。
讓我們說我有這樣一個泛型函數:C++驗證模板類型的可調用簽名
template<typename TFunc>
void templFunc(TFunc func) {
func(3, 6);
}
有什麼辦法在編譯的時候,我可以驗證TFunc的簽名,無論它是一個標準: :函數或任何類型的lambda或函數引用。我只是想確保TFunc的簽名爲void(int,int)或類似的static_assert,所以我可以產生一個非垃圾的錯誤消息。
您可以使用:
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
。如果函數不帶兩個參數,它將在調用func(3,6);
中失敗 - 兩次。一旦進入static_assert
行,並在下一行中進行一次。
如果參數類型爲int
或int
可以升級,轉換爲或轉換爲其他類型,則函數調用將成功。確保參數類型僅爲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
}
事情是這樣的作品使用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;
}
根據標準'dcl.dcl',「_在一個static_assert聲明中,常量表達式應該是一個常量表達式(5.19),可以是 **上下文**轉換爲bool ..._」...所以我想當模板依賴關係是**不包括在'static_assert'表達式中,那麼它可以在函數模板的上下文之外進行評估,這樣它就不依賴於函數模板是否被重載解析評估?它總是被評估? – mkal
對於定義爲auto a = [](long,int){}的lambda函數返回'true';' – user2296177
@ user2296177:是的,還有'double'或其他可以隱式轉換爲'int'的東西。當返回類型不正確時,它也只給出友好的錯誤消息,而不是當參數的數量或類型錯誤時... – mkal
所以我與一些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;
}
你確定這個工作正常嗎? 'auto_a = [](int,int){return 0;'is_signature
也不會給'auto a = [](int,char){};' – mkal
@ user2296177和@mkal錯誤 - 你是對的。如何將'auto b = [](int,int){return 0;}'分配給'std :: function
如果拉姆達是不要求那麼您可能使用'static_assert(標準:: is_same ::值, 「錯誤」);' –
iammilind