简化单个向量的 std::transform

Simplifying std::transform for single vector

我想编写一个简单的函数来简化 std::transform 对单个向量的转换。

到目前为止我得到了什么:

template<typename T>
void transform(std::vector<T> &vec, const std::function<T(const T &)> &fun) {
  std::transform(std::begin(vec), std::end(vec), std::begin(vec), fun);
}

现在当我想使用这个功能时,我可以这样写,例如:

transform<int>(vec, my_function);

但我更愿意使用

transform(vec, my_function);

有没有办法调整我的代码以自动推断类型?

错误:

no matching function for call to 'transform' and note: candidate template ignored:
could not match 'function<type-parameter-0-0 (const type-parameter-0-0 &)>' 
against '(lambda at [...]

您似乎在使用 lambda 作为参数; lambda 可以转换为 std::function,但在 template argument deduction 中不会考虑隐式转换,这就是它失败的原因。

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

您可以在传递 lambda 时使用 static_cast 明确地将其转换为 std::function,或者停止使用 std::function。例如

template<typename T, typename F>
void transform(std::vector<T> &vec, F fun) {
  std::transform(std::begin(vec), std::end(vec), std::begin(vec), fun);
}

你过度约束了两个参数,并尝试对第二个参数同时使用模板推导和隐式转换,这是行不通的。放弃它,并可选择使用 SFINAE,使事情正常工作,更高效和灵活:

using std::begin;
using std::end;

template <class Range, class F>
auto transform(Range&& range, F f)
noexcept(noexcept(std::transform(begin(range), end(range), begin(range), f)))
-> decltype(std::transform(begin(range), end(range), begin(range), f))
{ return std::transform(begin(range), end(range), begin(range), f); }

是的,函数对象被复制了。如果你不想这样,只需使用 std::ref 传递 std::reference_wrapper,就像标准库算法一样。

另一种使用 std::function 参数的方法,如果出于某种原因需要它,是通过将 T 移动到非推导上下文来禁用其中 T 的类型推导:

template<typename T>
struct Identity { using Type = T; };

template<typename T>
using Id = typename Identity<T>::Type;

template<typename T>
void transform(std::vector<T>&, const std::function<Id<T>(const Id<T>&)>&);