這其中的一半是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