推断可调用对象的第一个参数

Infer the first argument of a callable

本文关键字:第一个 参数 对象 调用      更新时间:2023-10-16

我希望能够推断可调用对象的第一个参数。我可以让它为免费和成员函数工作,但我正在与lambda作斗争。有什么我可以用的技巧吗?

这里有一个例子。在下面的match函数中,我想使用T的知识。

template<class T>
void match(void (*)(T*, int))   { /* First */ }
template<class T>
void match(void (T::*)(int))    { /* Second */ }
template<class T>
void match(std::function<void(T,int)>)    { /* Third */ }
struct A
{
   void f(int)  {}
};
void g(A*, int) {}
match(&A::f);           // Ok, matches first
match(&g);              // Ok, matches second
match([](A*, int) {});  // Not Ok
match([&](A*, int) {}); // Not Ok

你不能。

template<class T>
void g(T*, int) {}

无法工作

void g(void*, int) {}
void g(std::string**, int) {}

无法工作。

同样的问题也存在于lambdas中。

作为一般规则,您可以询问"can I invoke X with type Y",但您无法获得签名。

std::function不是λ, λ也不是std::function。它们是不相关的类型,除了您可以将lambda转换为具有任何兼容签名的std::function,就像您可以转换任何可调用对象一样。

如果你的问题空间足够有限,你可以写一个trait类来提取传入对象的operator()签名,并将其作为lambda的参数。

这在c++ 11中是一个坏主意,在c++ 14和c++ 17中通常会变得更糟。[](auto a, int b){}是c++ 14中的lambda(许多c++ 11编译器都支持它),它的第一个参数没有固定的类型。

通常更好的方法是将签名与可调用对象分开捆绑。这违反了c++ 11中的DRY (Don't Repeat Yourself),但在c++ 14中,lambda可以只接受auto&&参数。

另一种方法是问"这些类型中哪一个有效",这是可以做到的。通常,您所使用的类型并不是无限的,而是一个枚举集。

我知道只有一个办法:通过std::function

match(std::function<void(A*, int)>([](A*, int) {}));
match(static_cast<std::function<void(A*, int)>>([&](A*, int) {}));