Home > Mobile >  Transform/move elements from std::list to std::vector
Transform/move elements from std::list to std::vector

Time:03-15

I need to convert std::list< customStruct > to std::vector<std::string>. Is it possible to move all data from the customStruct::s_ into the vector of stings?

customStruct
{
    std::string s_;
};

Is it work for std::transform and std::make_move_iterator? Is this example correct? Will it really move strings?

std::list< customStruct > list;
std::vector< std::string> vt;

vt.reserve( list.size() );

std::transform( std::make_move_iterator( list.begin() ), std::make_move_iterator( list.end() ),
vt.begin(), [](const auto& el ) -> std::string
    {
        return std::move( el.s_ );
    } );

CodePudding user response:

Since your lambda takes [](const auto& el ) by const reference, the el is declared as const customStruct&, so el.s_ will be a const std::string (and the function will copy). You can instead take customStruct&& (or by universal reference auto&&). You can also not use move iterators and instead have your lambda take non-const lvalue references.

You also can't transform into vt.begin() because this will write to uninitailized elements (their memory only reserved, they are not actually constructed). Use std::back_inserter(vt) instead.

One example:

std::list< customStruct > list = /* something */;

std::vector<std::string> vt;
vt.reserve(list.size());
std::transform(list.begin(), list.end(), std::back_inserter(vt), [](auto& el) -> std::string&& {
    return std::move(el.s_);
});

CodePudding user response:

You should resize the vector before transforming into it. reserve only allocates capacity and then transform assigns to non-existing vector elements. Thats undefined behavior. Alternatively stay with reserve and use a back_inserter.

To see if it actually moves the elements you can add some output:

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


struct moveable {
    moveable() {}
    moveable(moveable&&){ std::cout << "move ctr\n"; }
    moveable& operator=(const moveable&){ return *this;}
    moveable(const moveable&) { std::cout << "copy ctr\n"; }
};

struct customStruct
{
    moveable m;
    std::string s_;
};
int main() {
std::list< customStruct > list;
list.push_back({});
list.push_back({});
list.push_back({});

std::vector< moveable > vt;

vt.resize( list.size() );

std::cout << "transform:\n";

std::transform( std::make_move_iterator( list.begin() ), std::make_move_iterator( list.end() ),
vt.begin(), [](const auto& el ) -> moveable
    {
        return std::move( el.m );
    } );

std::cout << "transform2\n";
std::transform( std::make_move_iterator( list.begin() ), std::make_move_iterator( list.end() ),
vt.begin(), [](auto&& el ) -> moveable
    {
        return std::move( el.m );
    } );

}

Output:

move ctr
move ctr
move ctr
transform:
copy ctr
copy ctr
copy ctr
transform2
move ctr
move ctr
move ctr

You cannot move from a const& thats why your code is not moving.

  • Related