如何在C ++模板中保持可调用参数的签名?

时间:2017-07-28 13:21:02

标签: c++ templates lambda template-deduction

下面的函数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调用吗?

2 个答案:

答案 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_tvoid_t很容易用C ++ 11编写,很容易在SO上找到。

可能存在错别字;在电话上。