如何使这些std :: function参数明确无误?

dan*_*jar 3 c++ lambda templates c++11 std-function

传递lambda时,以下函数重载是不明确的.我发现std::function可以从大多数可调用类型构建,即使它们的签名不匹配.所以编译器无法分辨使用哪个函数.

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);
Run Code Online (Sandbox Code Playgroud)

这里有一些类似的问题,但没有一个能解决我的问题.如何在不改变用法的情况下解决歧义?更重要的是,当时我必须明确提到模板类型.有没有解决的办法?

T.C*_*.C. 6

其中一半是LWG问题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){});
}
Run Code Online (Sandbox Code Playgroud)

使用一个实现LWG2132的库,这段代码打印出来,或许令人惊讶:

void each(std::function<void(T&, id)>) [with T = int]
Run Code Online (Sandbox Code Playgroud)

为什么?首先,可以构造一个std::function<void(T&, id)>from [](int, id){}.毕竟,后者可以用一个类型的左值来调用int.

第二,在

template <typename T> void each(std::function<void(T, id)>);
template <typename T> void each(std::function<void(T&, id)>);
Run Code Online (Sandbox Code Playgroud)

第二种方法比功能模板的部分排序规则更专业,因此总是通过重载分辨率来选择.


一种可能的解决方案是通过操纵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);
}
Run Code Online (Sandbox Code Playgroud)

这不适用于通用lambdas(它operator()是模板)或任意函数对象(可能有任意多次operator()重载).