这其中的一半是LWG issue 2132,从重载解析中移除std::function
的构造函数,除非参数实际上可以为指定的参数类型调用。这需要表达SFINAE支持才能实现,哪些VC++没有。
问题的另一半是重载解析:
#include<functional>
#include<iostream>
struct id {};
template <typename T> void each(std::function<void(T)>){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <typename T> void each(std::function<void(T, id)>){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <typename T> void each(std::function<void(T&)>){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <typename T> void each(std::function<void(T&, id)>){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main() {
each<int>([](int, id){});
}
用实现LWG2132,this code prints,也许令人惊讶的库:
void each(std::function<void(T&, id)>) [with T = int]
为什么?首先,可以从[](int, id){}
构建std::function<void(T&, id)>
。毕竟,后者可以被称为与int
类型的左值就好了。
其次,在
template <typename T> void each(std::function<void(T, id)>);
template <typename T> void each(std::function<void(T&, id)>);
第二个比第一个由函数模板部分排序规则更加专业化的,所以它总是通过重载决议选择。
一个可能的解决方案是通过操纵类型提取签名拉姆达的operator()
:
template<class T>
struct mem_fn_type;
template<class R, class C, class... T>
struct mem_fn_type<R(C::*)(T...)> {
using type = std::function<R(T...)>;
};
template<class R, class C, class... T>
struct mem_fn_type<R(C::*)(T...) const> {
using type = std::function<R(T...)>;
};
// optional extra cv-qualifier and ref-qualifier combos omitted
// since they will never be used with lambdas
// Detects if a class is a specialization of std::function
template<class T>
struct is_std_function_specialization : std::false_type {};
template<class T>
struct is_std_function_specialization<std::function<T>> : std::true_type{};
// Constrained to not accept cases where T is a specialization of std::function,
// to prevent infinite recursion when a lambda with the wrong signature is passed
template<class T>
typename std::enable_if<!is_std_function_specialization<T>::value>::type each(T func) {
typename mem_fn_type<decltype(&T::operator())>::type f = func;
each(f);
}
这不会对通用Lambda表达式工作(其operator()
是一个模板)或任意函数对象(可能会有任意多个过载)。
它不会匹配lambda表达式,编译器无法推导出'T'。 – Jamboree 2014-10-10 12:17:24
@Jamboree当我删除前三个重载,它编译罚款'manager.entity.each([=](type :: window&window,id entity){/ * ... * /} );' –
danijar
2014-10-10 12:20:22
哦,好的,所以你明确指定'T'。在C++ 14中,'std :: function'是sfinae友好的,所以它不会被那些不可调用的构造。 – Jamboree 2014-10-10 12:25:00