簡體   English   中英

C ++將Lambdas與向量內的Function指針區分開來

[英]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,它最終都是vectorstd::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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM