如何从参数列表中推导出函数对象的返回类型?

时间:2020-12-20 11:00:04

I'm trying to write a projection function that could transform a vector<T> into a vector<R>. Here is an example:

我正在尝试编写一个可以将vector 转换为vector 的投影函数。这是一个例子:

auto v = std::vector<int> {1, 2, 3, 4};
auto r1 = select(v, [](int e){return e*e; }); // {1, 4, 9, 16}
auto r2 = select(v, [](int e){return std::to_string(e); }); // {"1", "2", "3", "4"}

First attempt:

template<typename T, typename R>
std::vector<R> select(std::vector<T> const & c, std::function<R(T)> s)
{
   std::vector<R> v;
   std::transform(std::begin(c), std::end(c), std::back_inserter(v), s);
   return v;
}

But for

auto r1 = select(v, [](int e){return e*e; });

I get:

error C2660: 'select' : function does not take 2 arguments

错误C2660:'select':函数不带2个参数

I have to explicitly call select<int,int> to work. I don't like this because the types are redundant.

我必须显式调用select 才能工作。我不喜欢这个,因为类型是多余的。 ,int>

auto r1 = select<int, int>(v, [](int e){return e*e; }); // OK

Second attempt:

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;
}

The result is same error, function does not take 2 arguments. In this case I actually have to supply a 3rd type argument:

结果是同样的错误,函数不带2个参数。在这种情况下,我实际上必须提供第三种类型的参数:

auto r1 = select<int, int, std::function<int(int)>>(v, [](int e){return e*e; });

Third attempt:

template<typename T, typename R, template<typename, typename> class Selector>
std::vector<R> select(std::vector<T> const & c, Selector<T,R> s)
{
   std::vector<R> v;
   std::transform(std::begin(c), std::end(c), std::back_inserter(v), s);
   return v;
}

For

auto r1 = select<int, int, std::function<int(int)>>(v, [](int e){return e*e; });

the error is:

错误是:

'select' : invalid template argument for 'Selector', class template expected

'select':'Selector'的模板参数无效,期望的类模板

For

auto r1 = select(v, [](int e){return e*e; });

error C2660: 'select' : function does not take 2 arguments

错误C2660:'select':函数不带2个参数

(I know the last two attempts are not particularly great.)

(我知道最后两次尝试并不是特别好。)

How can I write this select() template function to work for the sample code I put in the beginning?

如何编写这个select()模板函数来处理我在开头放的示例代码?

2 个解决方案

#1


23  

Option #1:

Basic decltype() usage:

基本的decltype()用法:

template <typename T, typename F>
auto select(const std::vector<T>& c, F f)
    -> std::vector<decltype(f(c[0]))>
{
    using R = decltype(f(c[0]));
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c), std::back_inserter(v), f);
    return v;
}

Option #2:

Basic std::result_of<T> usage:

基本std :: result_of 用法:

template <typename T, typename F, typename R = typename std::result_of<F&(T)>::type>
std::vector<R> select(const std::vector<T>& c, F f)
{
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c), std::back_inserter(v), f);
    return v;
}

Option #3:

Advanced decltype() usage and perfect-forwarding (see notes*):

高级decltype()用法和完美转发(参见注释*):

template <typename T, typename A, typename F>
auto select(const std::vector<T, A>& c, F&& f)
    -> std::vector<typename std::decay<decltype(std::declval<typename std::decay<F>::type&>()(*c.begin()))>::type>
{
    using R = typename std::decay<decltype(std::declval<typename std::decay<F>::type&>()(*c.begin()))>::type;
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c)
                 , std::back_inserter(v)
                 , std::forward<F>(f));
    return v;
}

Option #4:

Advanced std::result_of<T> usage and perfect-forwarding (see notes*):

高级std :: result_of 用法和完美转发(参见注释*):

template <typename T, typename A, typename F, typename R = typename std::decay<typename std::result_of<typename std::decay<F>::type&(typename std::vector<T, A>::const_reference)>::type>::type>
std::vector<R> select(const std::vector<T, A>& c, F&& f)
{
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c)
                 , std::back_inserter(v)
                 , std::forward<F>(f));
    return v;
}

* Note: Options #3 and #4 assume that the std::transform algorithm takes a function object by-value, and then uses it as a non-const lvalue. This is why one can see this strange typename std::decay<F>::type& syntax. If the function object is supposed to be called within the select function itself, and the result type is not going to be used as a container's template argument (for the purpose of what the outer-most std::decay<T> is used), then the correct and portable syntax for obtaining the return type is:

*注意:选项#3和#4假设std :: transform算法采用函数对象by-value,然后将其用作非const左值。这就是为什么我们可以看到这个奇怪的typename std :: decay :: type&syntax。如果应该在select函数本身中调用函数对象,并且结果类型不会被用作容器的模板参数(出于使用最外层std :: decay 的目的) ,然后获取返回类型的正确和可移植语法是:

/*#3*/ using R = decltype(std::forward<F>(f)(*c.begin()));

/*#4*/ typename R = typename std::result_of<F&&(typename std::vector<T, A>::const_reference)>::type

#2


11  

Your first problem is that you think that a lambda is a std::function. A std::function and a lambda are unrelated types. std::function<R(A...)> is a type erasure object that can convert anything that is (A) copyable, (B) destroyable and (C) can be invoked using A... and returns a type compatible with R, and erases all other information about the type.

你的第一个问题是你认为lambda是一个std ::函数。 std :: function和lambda是不相关的类型。 std :: function 是一个类型擦除对象,可以转换任何(A)可复制,(B)可销毁和(C)可以使用A ...调用并返回兼容的类型使用R,并删除有关该类型的所有其他信息。 (a>

This means it can consume completely unrelated types, so long as they pass those tests.

这意味着它可以消耗完全不相关的类型,只要它们通过这些测试即可。

A lambda is an anonymous class which is destroyable, can be copied (except in C++14, where this is sometimes), and has an operator() which you specify. This means you can often convert a lambda into a std::function with a compatible signature.

lambda是一个可破坏的匿名类,可以复制(除了在C ++ 14中,有时也是这样),并且有一个你指定的operator()。这意味着您通常可以将lambda转换为具有兼容签名的std :: function。

Deducing the std::function from the lambda is not a good idea (there are ways to do it, but they are bad ideas: C++14 auto lambdas break them, plus you get needless inefficiency.)

从lambda中推导出std :: function并不是一个好主意(有很多方法可以做到这一点,但它们都是坏主意:C ++ 14自动lambdas会打破它们,加上你会得到不必要的低效率。)

So how do we solve your problem? As I see it, your problem is taking a function object and a container and deducing what kind of element transform would produce after applying the function object on each element, so you can store the result in a std::vector.

那么我们如何解决您的问题呢?在我看来,你的问题是获取一个函数对象和一个容器,并推断在每个元素上应用函数对象后会产生什么样的元素转换,因此你可以将结果存储在std :: vector中。

This is the answer that is closest to the solution to your problem:

这是最接近问题解决方案的答案:

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;
}

The easiest thing to do is swap T and R in template order, and have the caller pass in R explicitly, like select<double>. This leaves T and Selector being deduced. This isn't ideal, but it does do a small improvement.

最简单的方法是按模板顺序交换T和R,并让调用者明确地在R中传递,如select 。这使得T和Selector被推断出来。这不是理想的,但确实做了一点改进。

For a full solution, there are two ways to approach fixing this solution. First, we can change select to return a temporary object with an operator std::vector<R>, delaying the transformation to that point. Here is an incomplete sketch:

要获得完整的解决方案,有两种方法可以解决此解决方案。首先,我们可以更改select以使用运算符std :: vector 返回临时对象,从而将转换延迟到该点。这是一个不完整的草图:

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)};
}

I can also provide a slicker version that sadly relies on undefined behavior (reference capture of local references in a function have lifetime issues under the standard).

我还可以提供一个可靠地依赖于未定义行为的光滑版本(函数中本地引用的引用捕获在标准下具有生命周期问题)。

But that gets rid of auto v = select syntax -- you end up storing the thing that produces results, rather than the results.

但这摆脱了自动v =选择语法 - 你最终存储产生结果的东西,而不是结果。

You can still do std::vector<double> r = select( in_vec, [](int x){return x*1.5;} ); and it works pretty well.

你仍然可以做std :: vector r = select(in_vec,[](int x){return x * 1.5;});而且效果很好。

Basically I have split deduction into two phases, one for arguments, and one for return value.

基本上我将演绎拆分为两个阶段,一个用于参数,一个用于返回值。

However, there is little need to rely on that solution, as there are other more direct ways.

但是,没有必要依赖该解决方案,因为还有其他更直接的方法。

For a second approach, we can deduce R ourselves:

对于第二种方法,我们可以自己推导出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;
}

which is a pretty solid solution. A touch of cleanup:

这是一个非常可靠的解决方案。清理一下:

// 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;
}

makes this a serviceable solution. (moved R to template type lists, allowed alternative allocators to the vector, removed some needless std::, and did perfect forwarding on the Selector).

使这成为一个可用的解决方案。 (将R移动到模板类型列表,允许替代分配器到向量,删除一些不必要的std ::,并在Selector上完成转发)。

However, we can do better.

但是,我们可以做得更好。

The fact that the input is a vector is pretty pointless:

输入是向量的事实是毫无意义的:

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;
}

which doesn't compile due to the inability to determine T as yet. So lets work on that:

因为无法确定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;

which gives us an iterator<Range> and value_type<Iterator> aliases. Together they let us deduce T easily:

它为我们提供了一个迭代器 和value_type 别名。他们一起让我们轻松推断出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;
}

and bob is your uncle. (decayed_lvalue reflects how the Selector type is used for corner cases, and iterator<Range&> reflects that we are getting an iterator from the lvalue version of Range).

鲍勃是你的叔叔。 (decayed_lvalue反映了Selector类型如何用于转角情况,迭代器 反映了我们从Lvalue版本的Range获取迭代器)。 &>

In VS2013 sometimes the above decltypes confuse the half-implementation of C++11 they have. Replacing iterator<Range> with decltype(details::adl_begin(std::declval<Range>())) as ugly as that is can fix that issue.

在VS2013中,有时上面的decltypes会混淆他们拥有的C ++ 11的半实现。用decltype替换iterator (details :: adl_begin(std :: declval ()))就像那样丑陋可以解决这个问题。

// 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;
}

The resulting function will take arrays, vectors, lists, maps, or custom written containers, and will take any transformation function, and produce a vector of the resulting type.

生成的函数将采用数组,向量,列表,映射或自定义编写的容器,并将采用任何转换函数,并生成结果类型的向量。

The next step is to make the transformation lazy instead of putting it directly into a vector. You can have as_vector which takes a range and writes it out to a vector if you need to get rid of lazy evaluation. But that is getting into writing an entire library rather than solving your problem.

下一步是使转换变得懒惰而不是直接将其放入向量中。如果你需要摆脱懒惰的评估,你可以使用as_vector获取范围并将其写入向量。但是,这就是编写整个库而不是解决问题。

#1


23  

Option #1:

Basic decltype() usage:

基本的decltype()用法:

template <typename T, typename F>
auto select(const std::vector<T>& c, F f)
    -> std::vector<decltype(f(c[0]))>
{
    using R = decltype(f(c[0]));
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c), std::back_inserter(v), f);
    return v;
}

Option #2:

Basic std::result_of<T> usage:

基本std :: result_of 用法:

template <typename T, typename F, typename R = typename std::result_of<F&(T)>::type>
std::vector<R> select(const std::vector<T>& c, F f)
{
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c), std::back_inserter(v), f);
    return v;
}

Option #3:

Advanced decltype() usage and perfect-forwarding (see notes*):

高级decltype()用法和完美转发(参见注释*):

template <typename T, typename A, typename F>
auto select(const std::vector<T, A>& c, F&& f)
    -> std::vector<typename std::decay<decltype(std::declval<typename std::decay<F>::type&>()(*c.begin()))>::type>
{
    using R = typename std::decay<decltype(std::declval<typename std::decay<F>::type&>()(*c.begin()))>::type;
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c)
                 , std::back_inserter(v)
                 , std::forward<F>(f));
    return v;
}

Option #4:

Advanced std::result_of<T> usage and perfect-forwarding (see notes*):

高级std :: result_of 用法和完美转发(参见注释*):

template <typename T, typename A, typename F, typename R = typename std::decay<typename std::result_of<typename std::decay<F>::type&(typename std::vector<T, A>::const_reference)>::type>::type>
std::vector<R> select(const std::vector<T, A>& c, F&& f)
{
    std::vector<R> v;
    std::transform(std::begin(c), std::end(c)
                 , std::back_inserter(v)
                 , std::forward<F>(f));
    return v;
}

* Note: Options #3 and #4 assume that the std::transform algorithm takes a function object by-value, and then uses it as a non-const lvalue. This is why one can see this strange typename std::decay<F>::type& syntax. If the function object is supposed to be called within the select function itself, and the result type is not going to be used as a container's template argument (for the purpose of what the outer-most std::decay<T> is used), then the correct and portable syntax for obtaining the return type is:

*注意:选项#3和#4假设std :: transform算法采用函数对象by-value,然后将其用作非const左值。这就是为什么我们可以看到这个奇怪的typename std :: decay :: type&syntax。如果应该在select函数本身中调用函数对象,并且结果类型不会被用作容器的模板参数(出于使用最外层std :: decay 的目的) ,然后获取返回类型的正确和可移植语法是:

/*#3*/ using R = decltype(std::forward<F>(f)(*c.begin()));

/*#4*/ typename R = typename std::result_of<F&&(typename std::vector<T, A>::const_reference)>::type

#2


11  

Your first problem is that you think that a lambda is a std::function. A std::function and a lambda are unrelated types. std::function<R(A...)> is a type erasure object that can convert anything that is (A) copyable, (B) destroyable and (C) can be invoked using A... and returns a type compatible with R, and erases all other information about the type.

你的第一个问题是你认为lambda是一个std ::函数。 std :: function和lambda是不相关的类型。 std :: function 是一个类型擦除对象,可以转换任何(A)可复制,(B)可销毁和(C)可以使用A ...调用并返回兼容的类型使用R,并删除有关该类型的所有其他信息。 (a>

This means it can consume completely unrelated types, so long as they pass those tests.

这意味着它可以消耗完全不相关的类型,只要它们通过这些测试即可。

A lambda is an anonymous class which is destroyable, can be copied (except in C++14, where this is sometimes), and has an operator() which you specify. This means you can often convert a lambda into a std::function with a compatible signature.

lambda是一个可破坏的匿名类,可以复制(除了在C ++ 14中,有时也是这样),并且有一个你指定的operator()。这意味着您通常可以将lambda转换为具有兼容签名的std :: function。

Deducing the std::function from the lambda is not a good idea (there are ways to do it, but they are bad ideas: C++14 auto lambdas break them, plus you get needless inefficiency.)

从lambda中推导出std :: function并不是一个好主意(有很多方法可以做到这一点,但它们都是坏主意:C ++ 14自动lambdas会打破它们,加上你会得到不必要的低效率。)

So how do we solve your problem? As I see it, your problem is taking a function object and a container and deducing what kind of element transform would produce after applying the function object on each element, so you can store the result in a std::vector.

那么我们如何解决您的问题呢?在我看来,你的问题是获取一个函数对象和一个容器,并推断在每个元素上应用函数对象后会产生什么样的元素转换,因此你可以将结果存储在std :: vector中。

This is the answer that is closest to the solution to your problem:

这是最接近问题解决方案的答案:

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;
}

The easiest thing to do is swap T and R in template order, and have the caller pass in R explicitly, like select<double>. This leaves T and Selector being deduced. This isn't ideal, but it does do a small improvement.

最简单的方法是按模板顺序交换T和R,并让调用者明确地在R中传递,如select 。这使得T和Selector被推断出来。这不是理想的,但确实做了一点改进。

For a full solution, there are two ways to approach fixing this solution. First, we can change select to return a temporary object with an operator std::vector<R>, delaying the transformation to that point. Here is an incomplete sketch:

要获得完整的解决方案,有两种方法可以解决此解决方案。首先,我们可以更改select以使用运算符std :: vector 返回临时对象,从而将转换延迟到该点。这是一个不完整的草图:

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)};
}

I can also provide a slicker version that sadly relies on undefined behavior (reference capture of local references in a function have lifetime issues under the standard).

我还可以提供一个可靠地依赖于未定义行为的光滑版本(函数中本地引用的引用捕获在标准下具有生命周期问题)。

But that gets rid of auto v = select syntax -- you end up storing the thing that produces results, rather than the results.

但这摆脱了自动v =选择语法 - 你最终存储产生结果的东西,而不是结果。

You can still do std::vector<double> r = select( in_vec, [](int x){return x*1.5;} ); and it works pretty well.

你仍然可以做std :: vector r = select(in_vec,[](int x){return x * 1.5;});而且效果很好。

Basically I have split deduction into two phases, one for arguments, and one for return value.

基本上我将演绎拆分为两个阶段,一个用于参数,一个用于返回值。

However, there is little need to rely on that solution, as there are other more direct ways.

但是,没有必要依赖该解决方案,因为还有其他更直接的方法。

For a second approach, we can deduce R ourselves:

对于第二种方法,我们可以自己推导出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;
}

which is a pretty solid solution. A touch of cleanup:

这是一个非常可靠的解决方案。清理一下:

// 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;
}

makes this a serviceable solution. (moved R to template type lists, allowed alternative allocators to the vector, removed some needless std::, and did perfect forwarding on the Selector).

使这成为一个可用的解决方案。 (将R移动到模板类型列表,允许替代分配器到向量,删除一些不必要的std ::,并在Selector上完成转发)。

However, we can do better.

但是,我们可以做得更好。

The fact that the input is a vector is pretty pointless:

输入是向量的事实是毫无意义的:

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;
}

which doesn't compile due to the inability to determine T as yet. So lets work on that:

因为无法确定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;

which gives us an iterator<Range> and value_type<Iterator> aliases. Together they let us deduce T easily:

它为我们提供了一个迭代器 和value_type 别名。他们一起让我们轻松推断出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;
}

and bob is your uncle. (decayed_lvalue reflects how the Selector type is used for corner cases, and iterator<Range&> reflects that we are getting an iterator from the lvalue version of Range).

鲍勃是你的叔叔。 (decayed_lvalue反映了Selector类型如何用于转角情况,迭代器 反映了我们从Lvalue版本的Range获取迭代器)。 &>

In VS2013 sometimes the above decltypes confuse the half-implementation of C++11 they have. Replacing iterator<Range> with decltype(details::adl_begin(std::declval<Range>())) as ugly as that is can fix that issue.

在VS2013中,有时上面的decltypes会混淆他们拥有的C ++ 11的半实现。用decltype替换iterator (details :: adl_begin(std :: declval ()))就像那样丑陋可以解决这个问题。

// 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;
}

The resulting function will take arrays, vectors, lists, maps, or custom written containers, and will take any transformation function, and produce a vector of the resulting type.

生成的函数将采用数组,向量,列表,映射或自定义编写的容器,并将采用任何转换函数,并生成结果类型的向量。

The next step is to make the transformation lazy instead of putting it directly into a vector. You can have as_vector which takes a range and writes it out to a vector if you need to get rid of lazy evaluation. But that is getting into writing an entire library rather than solving your problem.

下一步是使转换变得懒惰而不是直接将其放入向量中。如果你需要摆脱懒惰的评估,你可以使用as_vector获取范围并将其写入向量。但是,这就是编写整个库而不是解决问题。