[英]C++ distinguish Lambdas from Function pointers inside a vector
我正在編寫一個小事件管理器類,我將一些函數指針存儲在向量中。 我使用std::function<void(int)>
作為矢量類型,我測試了在其中插入lambdas和普通函數,它的工作原理如下:
void t(int p){
/*things*/
}
[...]
event.bind([](int p){/*things*/});
event.bind(t);
現在, (在某一點上我需要刪除lambda而不是函數),我的問題是:
是否有可能將lambdas與函數區分開來? 如果有,怎么樣?
編輯:
由於我澄清了我的懷疑,這個問題就變成了標題所說的
真正的答案是:你不想這樣做。 如果您真的想知道原始類型,它也會失敗類型擦除仿函數。 這只是聞起來很糟糕的設計。
您可能正在尋找的是std::function::target_type
。 這是一種拉出function
對象正在存儲的目標函數的基礎type_info
的方法。 每個type_info
都有一個name()
,可以進行解碼。 請注意,這是一個非常深的兔子洞,你基本上將不得不硬編碼各種奇怪的邊緣情況。 我一直在感謝Yakk非常熱愛的幫助。
不同的編譯器以不同的方式破壞它們的lambda名稱,因此這種方法甚至不像可移植性。 快速檢查顯示clang
拋出一個$
而gcc
拋出{lambda...#d}
,所以我們可以通過編寫類似的東西來嘗試利用它:
bool is_identifier(std::string const& id) {
return id == "(anonymous namespace)" ||
(std::all_of(id.begin(), id.end(),
[](char c){
return isdigit(c) || isalpha(c) || c == '_';
}) && !isdigit(id[0]));
}
bool is_lambda(const std::type_info& info)
{
std::unique_ptr<char, decltype(&std::free)> own {
abi::__cxa_demangle(info.name(), nullptr, nullptr, nullptr),
std::free
};
std::string name = own ? own.get() : info.name();
// drop leading namespaces... if they are valid namespace names
std::size_t idx;
while ((idx = name.find("::")) != std::string::npos) {
if (!is_identifier(name.substr(0, idx))) {
return false;
}
else {
name = name.substr(idx+2);
}
}
#if defined(__clang__)
return name[0] == '$';
#elif defined(__GNUC__)
return name.find("{lambda") == 0;
#else
// I dunno?
return false;
#endif
}
然后把它扔進你的標准擦除 - 刪除成語:
void foo(int ) { }
void bar(int ) { }
long quux(long x) { return x; }
int main()
{
std::vector<std::function<void(int)>> v;
v.push_back(foo);
v.push_back(bar);
v.push_back(quux);
v.push_back([](int i) { std::cout << i << '\n';});
std::cout << v.size() << std::endl; // prints 4
v.erase(
std::remove_if(
v.begin(),
v.end(),
[](std::function<void(int)> const& f){
return is_lambda(f.target_type());
}),
v.end()
);
std::cout << v.size() << std::endl; // prints 3
}
不,不是一般的。
std::function<void(int)>
可以存儲一個函數指針,指向可以通過傳遞單個rvalue int
調用的任何函數。 有無數個這樣的簽名。
lambda的類型是每個聲明的唯一匿名類。 兩個不同的lambda不共享任何類型關系。
你可以確定std::function<void(int)>
存儲一個特定類型的變量,但是在函數指針和lambda情況下,有一個無限數量的不同類型可以存儲在std::function
考慮一下。 而且你只能測試“完全等於一種類型”。
您可以訪問類型ID信息,但那里沒有可移植的表示,並且通常將該信息用於身份匹配(和相關)或調試之外的任何事情都是一個壞主意。
現在,問題的限制版本(你能告訴std::function<void(int)>
包含void(*)(int)
類型的函數指針)很容易解決。 但總的來說,這樣做仍然是一個壞主意:首先,因為它很精致(代碼遠離你使用它的點,就像對函數簽名的微妙改變,可以破壞事物),其次,檢查和改變你的行為基於存儲在std::function
的類型應該只在極端情況下完成(通常涉及使用void*
style回調更新代碼到std::function
style callbacks)。
注意:這個答案預先假定有一個有限的,不同數量的函數簽名可以被指定為事件處理程序。 它假定使用錯誤的簽名分配任何舊函數是錯誤的。
您可以使用std :: function :: target來確定哪些是函數指針,並通過消除過程確定哪些必須是lambdas :
void func1(int) {}
void func2(double) {}
int main()
{
std::vector<std::function<void(int)>> events;
events.push_back(func1);
events.push_back([](int){});
events.push_back(func2);
for(auto& e: events)
{
if(e.target<void(*)(int)>())
std::cout << "funcion int" << '\n';
else if(e.target<void(*)(double)>())
std::cout << "funcion double" << '\n';
else
std::cout << "must be lambda" << '\n';
}
}
這是有效的,因為如果參數類型不匹配, std :: function :: target會返回空指針 。
單變量示例:
void func(int) {}
int main()
{
std::function<void(int)> f = func;
if(f.target<void(*)(int)>())
std::cout << "not a lambda" << '\n';
}
無論是函數指針還是lambda,它最終都是vector
的std::function<void(int)>
。 然后std::function<void(int)>
負責管理函數指針或lambda,而不是你的。 這意味着,您只需從vector
刪除所需的std::function<void(int)>
。 std::function<void(int)>
的析構std::function<void(int)>
知道如何正確地執行操作。 在你的情況下,這將無法使用函數指針並調用lambdas的析構函數。 std::function<void(int)>
使您能夠以漂亮和統一的方式處理不同的事物。 不要濫用它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.