C++将浮点数向量中的数据复制到一个浮点数对向量中。

3

我有一个目标std向量:

 std::vector<std::pair<float,float> > allVertices;

我希望使用pairs,因为每两个浮点数表示一个位置对(x,y)。现在,我有一个源std :: vector,其中包含所有这些位置,但作为浮点数组(称为m_vertices)。
我需要将所有数据从m_vertices复制到allVertices的末尾,并在复制过程中对数据进行转换。
我想到了std :: transformLambda,但我无法弄清如何将浮点向量复制到浮点对向量。
天真的方法:
    std::transform(m_vertices.begin(),m_vertices.end(),allVertices.end(),
                [](float x,float y)->std::pair<float,float>
            {
                return std::pair<float,float>(x * 100.0f,y * 100.0f)  ;
            }
            );

给我编译时错误:

error C2064: term does not evaluate to a function taking 1 arguments

还有一些难看的东西。

顺便说一下,如果有人能指出如何在不需要std::pair结构的情况下转换数据对,那对我的情况会更有帮助。

更新:

由于一些答案建议使用typical iterator,我想强调我真的希望看到功能性的解决方案。如果可能的话。


2
请注意,transform函数有一个接受两个迭代器的版本,这种情况下您的lambda表达式将起作用。然而,您需要确保这两个输入迭代器分别返回向量的交替元素,请参考https://dev59.com/2m025IYBdhLWcg3w_a-r。 - Steve Jessop
2个回答

6
编译器信息很明确:你的lambda必须接受一个输入参数,但是你的lambda接受了两个输入参数x和y。你不能使用std::transform完成你的任务,因为std::transform只能处理单个值并将其转换,而不能处理值对。以下是三种可能实现任务的方法:

普通命令式编程

为什么不像这样简单地使用普通的非函数式方法呢:
for(auto it = m_vertices.begin(); it != m_vertices.end();++it){
    float x = *it;
    ++it;
    float y = *it;
    all_vertices.emplace_back(x*100f,y*100f);
}

请确保m_vertices的大小是偶数;否则,这段代码当然会出问题。

虽然lambda和函数式编程很好,但有时候以命令式的方式实现更容易。

编写自己的对转换函数

以下是使用lambda编写执行缩减的函数的示例:

template< class InputIt, class OutputIt, class BinaryReducerOp >
OutputIt transformPairs( InputIt first1, InputIt last1, OutputIt d_first,
                    BinaryReducerOp reducer_op );
    for(auto it = first1; it != last1;++it){
        auto& x = *it;
        ++it;
        if(it == last1) throw; // Input length not even!
        auto& y = *it;
        *d_first++ = reducer_op(x,y);
    }
}

现在你可以将这个函数用于Lambda中。例如:
  transformPairs(m_vertices.begin(),m_vertices.end(),allVertices.end(),
                [](float x,float y)->std::pair<float,float>
            {
                return std::pair<float,float>(x * 100.0f,y * 100.0f)  ;
            }
            );

编写一对迭代器

正如 Steve Jessop 在他的评论中所正确指出的那样,编写自己的一对迭代器更加灵活,但也需要更多的工作。它可能看起来像这样(示意代码,此处没有编译器,可能包含一些小错误):

template<typename It> struct PairIterator {
private:
    mutable It it; // mutable so we can move around in operator*
public:
    typedef decltype(it*) Element;

    PairIterator(const It& it) : it(it) {}

    bool operator!=(const PairIterator<It>& other) const { return other != it; }

    std::pair<Element, Element> operator*() const {
        const Element& e1 = it*;
        ++it;
        const Element& e2 = it*;
        --it;
        return std::make_pair(e1,e2);
    }

    PairIterator<It>& operator++(){
        ++it;
        ++it;
        return *this;
    } 
}

template<typename It>
make_pair_it(const It& it){ return PairIterator<It>(it); }

现在你可以像这样使用std::transform
std::transform(make_pair_it(m_vertices.begin()),make_pair_it(m_vertices.end()),allVertices.end(),
                    [](std::pair<float,float> p)->std::pair<float,float>
                {
                    return std::pair<float,float>(p.first * 100.0f,p.second * 100.0f)  ;
                }
                );

那么有什么替代方案吗?我需要将向量中的每两个数字进行转换。 - Michael IV
@MichaelIV:我猜标准功能不支持这个。原因是将相邻的两个元素合并成一个在实际应用中并不是那么常见,因此没有被纳入STL。如果您在代码中经常需要使用这种情况,且变换不同,可以使用我的代码构建一个接受Lambda表达式编码转换的函数。然后就可以使用Lambda表达式了。我将添加一些示例代码,请稍等。 - gexicide
哇,好棒的技巧。你认为你的哪个解决方案会更快? - Michael IV
@MichaelIV:好问题;通过足够的优化,两者可能同样快。没有优化的情况下,第一个应该更快,因为成对值被放置而不是复制到向量中。但未经优化的性能并不重要;优化后的性能才是最重要的。只需测量它;我猜想使用-O3你不应该看到很大的差异。 - gexicide
3
注意:不要发明一个名为transformPairs的算法,而是可以发明一个迭代器适配器pair_iterator,它将从其底层迭代器返回连续元素对(pairs)。虽然这样需要更多工作,但如果您还想以这种方式使用除transform之外的其他算法,则会产生回报。 - Steve Jessop
显示剩余2条评论

0

对于你的问题,由于两个向量中的内存数组是相同的,你可以直接复制内存,这应该是最快的解决方案。

请注意,在将此应用于另一个情况之前,必须确保自己知道自己在做什么,否则很容易成为错误的源头。如果尝试复制不同类型(例如从double到float),它将无法工作。

allVertices.resize(m_vertices.size() / 2u);
std::copy_n(m_vertices.data(), m_vertices.size(), &(allVertices.front().first));

如果m_vertices的大小不是偶数,这段代码也会出错。

然后,您可以使用基于范围的for循环来应用您的处理方法。

for (auto & pair: allVertices)
    treatment(pair);

你可以直接复制内存,这意味着目标向量不会分配新的内存?但在这种情况下,数据的转换将影响初始向量,因此这可能不是一个选项,因为源向量数据必须保持不变。 - Michael IV
当然不是,内存是在“resize”函数中分配的,然后通过copy_n(使用memmove)高效地复制。这避免了像其他一些解决方案中创建临时变量的情况。 - peroket

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接