传递lambda时,以下函数重载是不明确的。我发现std::function
可以是constructed from most callable types,即使他们的签名不匹配也是如此。因此,编译器无法告诉使用哪个函数。
template <typename T> void each(std::function<void(T)> iterator);
template <typename T> void each(std::function<void(T, id)> iterator);
template <typename T> void each(std::function<void(T&)> iterator);
template <typename T> void each(std::function<void(T&, id)> iterator);
这里有一些类似的问题,但没有一个能解决我的问题。如何在不改变用法的情况下解决歧义?更重要的是,当时我必须明确提到模板类型。有办法解决这个问题吗?
答案 0 :(得分:6)
其中一半是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]
为什么呢?首先,可以从std::function<void(T&, id)>
构建[](int, id){}
。毕竟,后者可以用int
类型的左值调用就好了。
第二,在
template <typename T> void each(std::function<void(T, id)>);
template <typename T> void each(std::function<void(T&, id)>);
第二种方法比功能模板的部分排序规则更专业,因此它总是通过重载分辨率来选择。
一种可能的解决方案是通过操纵lambda 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()
是模板)或任意函数对象(可能有任意多operator()
次重载)。
答案 1 :(得分:1)
Morever,当时我必须明确提到模板类型。有办法解决这个问题吗?
对此的规范解决方案是实现通用依赖注入点(即单个重载)并允许客户端代码决定它放在那里的内容。我不确定如何给出一个对你提供的代码有意义的例子,因为我无法想象一个名为each
的函数会对一个名为iterator的参数做什么(当该参数是一个返回{{1}的仿函数时})。
类似于你的功能将应用于访客模式,所以我将举例说明:
void
客户代码:
class collection {
std::vector<int> data; // to be visited
public:
void visit(std::function<void(int)> visitor) // single overload
{
std::for_each(std::begin(data), std::end(data), visitor);
}
};
void complex_visitor(int element, double EXTRA, char* PARAMETERS, bool HERE);
在此示例中,您决定如何插入不匹配的访问者,而不是collection c;
char* data = get_some_data();
bool a = false;
c.visit( [&](int x) { complex_visitor(x, .81, data, a); } );
的界面中的最后一行(即在客户端代码中)类。