下面的函数f()
采用可调用的参数fc
,其签名在函数定义中很好地可见。
// scaffolding for function definition
template<typename T>
struct Identity {
using type = T;
};
// still scaffolding for function definition
template<typename T>
using Make_deducible = typename Identity<T>::type;
// function definiton
template <typename T>
T f(T val, Make_deducible<std::function<T(T)>> fc) // we see fc's expected signature
{ return fc(val); }
请注意,可以通过以下所有方式调用它:
int g(int i) { return i * i; };
f(5, g); // call with function ptr: rarely used
std::function<int(int)> fo{[](int i){ return i * i; }};
f(6, fo); // call with std::function object: tedious
f(7, [](int i){ return i * i; }); // call with lambda closure: usual use case
困扰我的是f()
的定义需要一些脚手架才能工作。所以,我的问题是:
是否有少迂回方式执行此操作,而在定义中保持get_val
可见的签名,并且仍然让函数可以用lambda调用吗?
答案 0 :(得分:1)
您可以检查可调用对象是否可转换为std::function<T(T)>
:
template<class T, class F>
std::enable_if_t<std::is_convertible<F, std::function<T(T)>>::value, T>
f(T v, F fn) {
return fn(v);
}
答案 1 :(得分:-1)
让deducible阻止编译器尝试tomdeduce该参数。这似乎是一个糟糕的名字选择;通常称为block_deduction
或其他东西。
包含签名的最简单方法是注释:
template <class T, class F /*T(T) signature*/>
T f(T val, F fc)
{ return fc(val); }
无论你想要什么。
如果我们想要编译器强制执行,我们可以像你在问题中那样阻止扣除和类型擦除。另一种选择:
template<class Sig, class F, class=void>
struct can_call:std::false_type{};
template<class R, class...Args, class F>
struct can_call<R(Args...),F,std::enable_if_t<
std::is_convertible<std::result_of_t<F(Args...)>,R>{}
>>:std::true_type{};
template<class...Args, class F>
struct can_call<void(Args...),F,std::void_t<
std::result_of_t<F(Args...)>,R>{}
>>:std::true_type{};
定义can_call<Sig, F>
当且仅当您可以在与Sig
兼容的上下文中调用F时才会为真。
template <class T, class F /*T(T) signature*/>
std::enable_if_t<can_call<T(T),F&>{}, T> T f(T val, F fc)
{ return fc(val); }
现在F
在重载解析时检查,以确保它兼容。
我使用了一些C ++ 14和C ++ 17 std::void_t
。 void_t
很容易用C ++ 11编写,很容易在SO上找到。
可能存在错别字;在电话上。