您的第一个问题是您认为 lambda 是 std::function。 std::function 和 lambda 是不相关的类型。 std::function<R(A...)> 是一个类型擦除对象,它可以转换任何 (A) 可复制、(B) 可销毁和 (C) 可以使用 A... 调用并返回与 R 兼容的类型,并擦除有关的所有其他信息类型。
这意味着它可以使用完全不相关的类型,只要它们通过了这些测试。
lambda 是一个可销毁的匿名类,可以复制(C++14 除外,有时会复制),并具有您指定的operator()。这意味着您通常可以将 lambda 转换为具有兼容签名的 std::function。
从 lambda 推导出 std::function 不是一个好主意(有办法做到这一点,但它们是坏主意:C++14 auto lambda 破坏了它们,而且你会得到不必要的低效率。)
那么我们如何解决您的问题?在我看来,您的问题是获取一个函数对象和一个容器,并推断在将函数对象应用于每个元素后会产生什么样的元素 transform,因此您可以将结果存储在 std::vector 中。
这是最接近您问题解决方案的答案:
template<typename T, typename R, typename Selector>
std::vector<R> select(std::vector<T> const & c, Selector s) {
std::vector<R> v;
std::transform(std::begin(c), std::end(c), std::back_inserter(v), s);
return v;
}
最简单的方法是按模板顺序交换T 和R,并让调用者显式传入R,例如select<double>。这样就可以推断出T 和Selector。这并不理想,但确实有一点改进。
对于完整的解决方案,有两种方法可以解决此解决方案。首先,我们可以更改select 以返回一个带有operator std::vector<R> 的临时对象,从而将转换延迟到该点。这是一个不完整的草图:
template<typename T, typename Selector>
struct select_result {
std::vector<T> const& c;
Selector s;
select_result(select_result&&)=default;
select_result(std::vector<T> const & c_, Selector&& s_):
c(c_), s(std::forward<Selector>(s_)
{}
operator std::vector<R>()&& {
std::vector<R> v;
std::transform(std::begin(c), std::end(c), std::back_inserter(v), s);
return v;
}
};
template<typename T, typename Selector>
select_result<T, Selector> select(std::vector<T> const & c, Selector&& s) {
return {c, std::forward<Selector>(s)};
}
我还可以提供a slicker version,它可悲地依赖于未定义的行为(函数中本地引用的引用捕获在标准下存在生命周期问题)。
但这摆脱了auto v = select 语法——你最终会存储产生结果的东西,而不是结果。
您仍然可以使用std::vector<double> r = select( in_vec, [](int x){return x*1.5;} );,并且效果很好。
基本上我将推导分为两个阶段,一个用于参数,一个用于返回值。
但是,几乎不需要依赖该解决方案,因为还有其他更直接的方法。
对于第二种方法,我们可以自己推导出R:
template<typename T, typename Selector>
std::vector<typename std::result_of<Selector(T)>::type>
select(std::vector<T> const & c, Selector s) {
using R = typename std::result_of<Selector(T)>::type;
std::vector<R> v;
std::transform(std::begin(c), std::end(c), std::back_inserter(v), s);
return v;
}
这是一个非常可靠的解决方案。稍微清理一下:
// std::transform takes by-value, then uses an lvalue:
template<class T>
using decayed_lvalue = typename std::decay<T>::type&;
template<
typename T, typename A,
typename Selector,
typename R=typename std::result_of<decayed_lvalue<Selector>(T)>::type
>
std::vector<R> select(std::vector<T, A> const & c, Selector&& s) {
std::vector<R> v;
std::transform(begin(c), end(c), back_inserter(v), std::forward<Selector>(s));
return v;
}
使它成为一个可用的解决方案。 (将R 移动到template 类型列表,允许vector 的替代分配器,删除一些不必要的std::,并在Selector 上进行了完美转发。
但是,我们可以做得更好。
输入是vector这一事实毫无意义:
template<
typename Range,
typename Selector,
typename R=typename std::result_of<Selector(T)>::type
>
std::vector<R> select(Range&& in, Selector&& s) {
std::vector<R> v;
using std::begin; using std::end;
std::transform(begin(in), end(in), back_inserter(v), std::forward<Selector>(s));
return v;
}
由于无法确定T 而无法编译。所以让我们继续努力吧:
namespace details {
namespace adl_aux {
// a namespace where we can do argument dependent lookup on begin and end
using std::begin; using std::end;
// no implementation, just used to help with ADL based decltypes:
template<class R>
decltype( begin( std::declval<R>() ) ) adl_begin(R&&);
template<class R>
decltype( end( std::declval<R>() ) ) adl_end(R&&);
}
// pull them into the details namespace:
using adl_aux::adl_begin;
using adl_aux::adl_end;
}
// two aliases. The first takes a Range or Container, and gives
// you the iterator type:
template<class Range>
using iterator = decltype( details::adl_begin( std::declval<Range&>() ) );
// the second is syntactic sugar on top of `std::iterator_traits`:
template<class Iterator>
using value_type = typename std::iterator_traits<Iterator>::value_type;
这给了我们iterator<Range> 和value_type<Iterator> 别名。他们一起让我们轻松推断T:
// std::transform takes by-value, then uses an lvalue:
template<class T>
using decayed_lvalue = typename std::decay<T>::type&;
template<
typename Range,
typename Selector,
typename T=value_type<iterator<Range&>>,
typename R=typename std::result_of<decayed_lvalue<Selector>(T)>::type
>
std::vector<R> select(Range&& in, Selector&& s) {
std::vector<R> v;
using std::begin; using std::end;
std::transform(begin(in), end(in), back_inserter(v), std::forward<Selector>(s));
return v;
}
和bob is your uncle。 (decayed_lvalue 反映了 Selector 类型如何用于极端情况,iterator<Range&> 反映我们从 Range 的左值版本获取迭代器。
在 VS2013 中,有时上面的 decltypes 会混淆他们拥有的 C++11 的半实现。用 decltype(details::adl_begin(std::declval<Range>())) 替换 iterator<Range> 就可以解决这个问题。
// std::transform takes by-value, then uses an lvalue:
template<class T>
using decayed_lvalue = typename std::decay<T>::type&;
template<
typename Range,
typename Selector,
typename T=value_type<decltype(details::adl_begin(std::declval<Range&>()))>,
typename R=typename std::result_of<decayed_lvalue<Selector>(T)>::type
>
std::vector<R> select(Range&& in, Selector&& s) {
std::vector<R> v;
using std::begin; using std::end;
std::transform(begin(in), end(in), back_inserter(v), std::forward<Selector>(s));
return v;
}
结果函数将采用数组、向量、列表、映射或自定义编写的容器,并将采用任何转换函数,并生成结果类型的向量。
下一步是让转换变得惰性,而不是直接放入vector。如果您需要摆脱惰性求值,您可以使用 as_vector 获取范围并将其写入向量。但这是在编写整个库而不是解决您的问题。