C++ C++;区分lambda和向量中的函数指针
我正在编写一个小的事件管理器类,其中我将一些函数指针存储在一个向量中。我使用C++ C++;区分lambda和向量中的函数指针,c++,function,c++11,lambda,C++,Function,C++11,Lambda,我正在编写一个小的事件管理器类,其中我将一些函数指针存储在一个向量中。我使用std::function作为向量类型,我测试了在其中插入lambdas和普通函数,它可以工作: void t(int p){ /*things*/ } [...] event.bind([](int p){/*things*/}); event.bind(t); 现在,(在某一点上,我需要删除lambda,但不需要删除函数),我的问题是: 是否可以区分lambda和函数?如果是,如何进行? 编辑: 既然我澄清了我
std::function
作为向量类型,我测试了在其中插入lambdas和普通函数,它可以工作:
void t(int p){
/*things*/
}
[...]
event.bind([](int p){/*things*/});
event.bind(t);
现在,(在某一点上,我需要删除lambda,但不需要删除函数),我的问题是:
是否可以区分lambda和函数?如果是,如何进行?
编辑:既然我澄清了我的疑问,这个问题就变成了标题所说的问题。真正的答案是:你不想这样做。如果你真的想知道原始的类型,不管发生什么情况,它都会破坏类型擦除函子的功能。这闻起来像是糟糕的设计
您可能正在寻找的是。这是一种拉出
函数
对象正在存储的目标函数的基础类型信息
的方法。每个类型\u info
都有一个名称()
,可以按需设置。请注意,这是一个非常深的兔子洞,您基本上需要硬编码各种奇怪的边缘情况。多亏了雅克的大力帮助,我一直在做这件事
不同的编译器对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
}
bool是_标识符(std::string const&id){
返回id==(匿名命名空间)||
(std::所有(id.begin()、id.end()、的,
[](字符c){
返回isdigit(c)| | isalpha(c)| | c=='|';
})&&!isdigit(id[0]);
}
布尔是λ(常数标准::类型信息和信息)
{
标准::唯一的{
abi::_cxa_demangle(info.name(),nullptr,nullptr,nullptr),
免费
};
std::string name=own?own.get():info.name();
//删除前导命名空间…如果它们是有效的命名空间名称
标准:尺寸(idx);
while((idx=name.find(“::”)!=std::string::npos){
如果(!是_标识符(name.substr(0,idx))){
返回false;
}
否则{
name=name.substr(idx+2);
}
}
#如果已定义(_u-clang__;)
返回名称[0]='$';
#已定义的elif(_GNUC__)
返回name.find(“{lambda”)==0;
#否则
//我不知道?
返回false;
#恩迪夫
}
然后在你的标准删除习惯用语中加入:
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
}
voidfoo(int){}
空条(int){}
长quux(长x){返回x;}
int main()
{
std::向量v;
v、 推回(foo);
v、 推回(巴);
v、 推回(quux);
v、 push_back([](int i){std::cout注意:此答案假定有有限个不同数量的函数签名可以指定为事件处理程序。它假定使用错误签名分配任何旧函数都是错误的
您可以使用确定哪些是函数指针,并通过消除过程确定哪些必须是lambda:
void func1(int){}
void func2(double){}
int main()
{
向量事件;
事件。推回(功能1);
事件。推回([](int){});
事件。推回(func2);
用于(自动和e:事件)
{
if(如target())
std::cout无论是函数指针还是lambda,它最终都会成为vector
中的std::function
。然后std::function
负责管理函数指针或lambda,而不是您的。这意味着,您只需从vector
中删除所需的std::function
。函数的析构函数e> std::function
知道如何正确处理问题。在您的情况下,这意味着不使用函数指针和调用lambdas的析构函数。std::function
使您能够以一种友好和统一的方式处理不同的事情。不要误用它。不,不是一般情况
std::function
可以存储指向任何函数的函数指针,该函数可以通过传递单个rvalueint
来调用。此类签名的数量是无限的
lambda的类型是每个声明的唯一匿名类。两个不同的lambda不共享任何类型关系
您可以确定一个代码> STD::函数< /COD>存储一个特定类型的变量,但是在函数指针和lambda情况下,有一个无限数量的不同类型可以存储在<代码> STD::函数< /代码>中。并且只能测试“完全等于类型”。
您可以访问类型id信息,但那里没有可移植的表示,通常将该信息用于身份匹配(和相关)或调试之外的任何事情都是一个坏主意
现在,这个问题的限制版本(你能告诉我一个std::function
是否包含一个void(*)(int)
)类型的函数指针)很容易解决。但总的来说,这样做仍然是个坏主意:首先,因为它很微妙(代码远离您使用它的地方,就像对函数签名的细微更改一样,可能会破坏一些东西),其次,根据存储在std::function
中的类型检查和更改您的行为只应在极端情况下进行(通常涉及将代码从使用void*
style回调更新为std::function
style回调).什么意思,删除lambda?为什么lambda被视为不同于函数指针?您的设计看起来…错误。您可以将带有bool
的std::pair
作为第二个参数,而bool
将指示它是函数指针还是lambda。如果您想区分ambda和函数指针,不要把它们放在一个向量中,以同样的方式对待它们…(P
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';
}
}
void func(int) {}
int main()
{
std::function<void(int)> f = func;
if(f.target<void(*)(int)>())
std::cout << "not a lambda" << '\n';
}