How do I set std :: vector with another, where two are vectors of different classes?

Is there a way to set std :: vector with another, where two are vectors of different classes?

struct A {
   int a;
};

struct B {
   int a, b;
   B(int _a, int _b) : a(_a), b(_b) {}
};

int commonB = 123;

std::vector<A> va;
std::vector<B> vb;

// Fill va...

// Code I would like to optimise:
for(size_t i = 0; i < va.size(); ++i) {
    vb.push_back(B(va[i].a, commonB));
}

      

Anything you want (pseudocode):

vb = va;

      

with uninitialized values ​​B :: b?

+3


source to share


4 answers


I would like to point out that since for ranges based on ranges, I had little desire to use simple algorithms such as converting very often. Hope you can understand why.



std::transform(va.begin(), va.end(), std::back_inserter(vb),
    [commonB](A x) -> B { return B(x.a, commonB); });

for (auto& e : va)
    vb.emplace_back(e.a, commonB);

      

+5


source


transform

from <algorithm>

header along with back_inserter

from <iterator>

can be used to do it like this:

std::transform(va.begin(), va.end(), std::back_inserter(vb),
    [](A x) -> B { return B(x.a, commonB); });

      

UPDATE



If it commonB

does not have a global scope, its name should be written (written in square brackets of the lambda definition):

std::transform(va.begin(), va.end(), std::back_inserter(vb),
    [commonB](A x) -> B { return B(x.a, commonB); });

      

+3


source


I don't think you can get much more optimal. You can make sure enough space is reserved in vb

upfront to avoid unnecessary reallocations and use a loop for the loop and emplace_back

to make the loop more concise:

vb.reserve(va.size());
for (const auto& a : va)
    vb.emplace_back(a, commonB);

      

+1


source


You can generalize with a transform function and a lambda.

Everything will be perfectly optimized when compiled with -O2 or better:

Note the use reserve()

to prevent reallocation when the vector grows.

#include <vector>
#include <algorithm>

struct A {
   int a;
};

struct B {
   int a, b;
   B(int _a, int _b) : a(_a), b(_b) {}
};

template<class TransformFunction>
std::vector<B> transform_copy(std::vector<A>& source, TransformFunction&& trans)
{
    std::vector<B> result;
    result.reserve(source.size());
    std::transform(source.begin(), source.end(), std::back_inserter(result), trans);
    return result;
}

int main()
{
    int commonB = 123;
    auto transform_function = [commonB](A const& source) {
        return B(source.a, commonB);
    };

    std::vector<A> va;
    std::vector<B> vb = transform_copy(va, transform_function);

}

      

You can also make it more expressive. Lambdas are a simple and optimally efficient way of expressing human-readable concepts such as lazy functions (e.g. Haskell et al.):

auto vb = transform_to_Bs(va, with_common_b(123));

      

Which can be implemented this way:

#include <vector>
#include <algorithm>

struct A {
    int a;
};

struct B {
    int a, b;

    B(int _a, int _b) : a(_a), b(_b) {}
};


extern std::vector<A> get_As();

int main() {

    auto with_common_b = [](auto &&commonB) {
        return [commonB](auto &&a) {
            return B(a.a, commonB);
        };
    };

    auto transform_to = [](auto &&target, auto &&source, auto &&transform_function) {
        target.reserve(source.size());
        std::transform(source.begin(), source.end(),
                       std::back_inserter(target),
                       std::forward<decltype(transform_function)>(transform_function));
    };

    auto transform_to_Bs = [transform_to](auto &&va, auto &&transform_function) {
        std::vector<B> result;
        transform_to(result, va, std::forward<decltype(transform_function)>(transform_function));
        return result;
    };

    std::vector<A> va = get_As();

    auto vb = transform_to_Bs(va, with_common_b(123));
}

      

Applying perfect forwarding here is a gross over-optimization. The compiler actually keeps all redundant copies of function objects.

+1


source







All Articles