仅当 (a) 您正在调用函数/运算符的名称,或 (b) 将其强制转换为具有显式签名的指针(指向函数或成员函数)时,才会发生重载解析。
这里都没有发生。
std::function 接受任何与其签名兼容的对象。它不需要专门的函数指针。 (lambda 不是 std 函数,std 函数也不是 lambda)
现在在我的自制函数变体中,对于签名 R(Args...),我也接受 R(*)(Args...) 参数(完全匹配)正是出于这个原因。但这意味着它将“完全匹配”签名提升到“兼容”签名之上。
核心问题是重载集不是 C++ 对象。您可以命名重载集,但不能“本地”传递它。
现在,您可以像这样创建一个函数的伪重载集:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) )
这会创建一个可以对函数名进行重载解析的单个 C++ 对象。
扩展宏,我们得到:
[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }
这写起来很烦人。这里有一个更简单但用处稍差的版本:
[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }
我们有一个接受任意数量参数的 lambda,然后将它们完美转发到 baz。
然后:
class Bar {
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};
有效。我们将重载解析推迟到我们存储在 fun 中的 lambda 中,而不是直接传递 fun 重载集(它无法解析)。
至少有一个提议要在 C++ 语言中定义一个将函数名转换为重载集对象的操作。在这样的标准提案出现在标准中之前,OVERLOADS_OF 宏很有用。
您可以更进一步,支持强制转换为兼容函数指针。
struct baz_overloads {
template<class...Ts>
auto operator()(Ts&&...ts)const
RETURNS( baz(std::forward<Ts>(ts)...) );
template<class R, class...Args>
using fptr = R(*)(Args...);
//TODO: SFINAE-friendly support
template<class R, class...Ts>
operator fptr<R,Ts...>() const {
return [](Ts...ts)->R { return baz(std::forward<Ts>(ts)...); };
}
};
但这开始变得迟钝了。
Live example.
#define OVERLOADS_T(...) \
struct { \
template<class...Ts> \
auto operator()(Ts&&...ts)const \
RETURNS( __VA_ARGS__(std::forward<Ts>(ts)...) ); \
\
template<class R, class...Args> \
using fptr = R(*)(Args...); \
\
template<class R, class...Ts> \
operator fptr<R,Ts...>() const { \
return [](Ts...ts)->R { return __VA_ARGS__(std::forward<Ts>(ts)...); }; \
} \
}