5

我想map()在 C++ 中模仿 Ruby 的方法。我正在努力自动找出返回类型:

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

typedef std::string T2;

template<class T1,
//  class T2, // gives "couldn't deduce template parameter 'T2'"
    class UnaryPredicate>
std::vector<T2> map(std::vector<T1> in, UnaryPredicate pred)
{
    std::vector<T2> res(in.size());
    std::transform(in.begin(), in.end(), res.begin(), pred);
    return res;
}

int main()
{
    std::vector<int> v1({1,2,3});
    auto v2(map(v1, [](auto el) { return "'"+std::to_string(el+1)+"'"; }));
    std::cout << v2[0] << "," << v2[1] << "," << v2[2] << std::endl;
}

这样它可以编译,但T2固定为string. 如果我使用其他T2定义,编译器会抱怨couldn't deduce template parameter 'T2'。我也尝试使用std::declval,但可能不是正确的方法 - 我无法解决问题。

4

2 回答 2

10

使用decltype+ std::decay_t

template <class T, class UnaryPredicate>
auto map(const std::vector<T>& in, UnaryPredicate pred)
{
    using result_t = std::decay_t<decltype(pred(in[0]))>;

    std::vector<result_t> res;
    res.reserve(in.size());
    std::transform(in.begin(), in.end(), std::back_inserter(res), pred);
    return res;
}

示例用法:

std::vector v1{1, 2, 3};
auto v2 = map(v1, [](int el) { return "'" + std::to_string(el + 1) + "'"; });
std::cout << v2[0] << ", " << v2[1] << ", " << v2[2] << '\n';

现场演示

另请注意我所做的以下更改:

  1. 我改为in通过 const 引用而不是按值获取。这避免了不必要的复制。

  2. 我使用reserve+ back_inserter,而不是值初始化 + 赋值。

  3. 我用作auto返回类型。这将启用返回类型扣除。res保证向量不会被复制。它也有资格进行复制省略。

  4. 您可以直接从花括号初始化列表中列出初始化因此请删除围绕花括号的括号。

  5. std::endl\n足够 时不应使用。std::endl导致缓冲区被刷新,而\n不会。不必要的刷新会导致性能下降。见std::endlvs\n

于 2019-11-10T10:36:17.177 回答
4

为了简化auto作为返回类型value_type的使用,可以通过declval- call UnaryPredicatefor来指定 vector 的使用T1

template<class T1,
    class UnaryPredicate>
auto map(std::vector<T1> in, UnaryPredicate pred)
{
    std::vector< decltype(std::declval<UnaryPredicate>()(T1{})) > res(in.size());
    std::transform(in.begin(), in.end(), res.begin(), pred);
    return res;
}

演示

于 2019-11-10T10:34:17.940 回答