Construction a vector from the concatenation of 2

2019-02-24 01:33发布

问题:

Is there a way to construct a vector as the concatenation of 2 vectors (Other than creating a helper function?)

For example:

const vector<int> first = {13};
const vector<int> second = {42};
const vector<int> concatenation = first + second;

I know that vector doesn't have an addition operator like string, but that's the behavior that I want. Such that concatenation would contain: 13 and 42.

I know that I can initialize concatenation like this, but it prevents me from making concatenation const:

vector<int> concatenation = first;
first.insert(concatenation.end(), second.cbegin(), second.cend()); 

回答1:

No, it's not possible if you require that

  • no helper function is defined, and
  • the resulting vector can be declared const.


回答2:

template<typename T>
std::vector<T> operator+(const std::vector<T>& v1, const std::vector<T>& v2){
    std::vector<T> vr(std::begin(v1), std::end(v1));
    vr.insert(std::end(vr), std::begin(v2), std::end(v2));
    return vr;
}

This does require a helper "function", but at least it allows you to use it as

const vector<int> concatenation = first + second;


回答3:

I think you have to write a help function. I'd write it as:

std::vector<int> concatenate(const std::vector<int>& lhs, const std::vector<int>& rhs)
{
    auto result = lhs;
    std::copy( rhs.begin(), rhs.end(), std::back_inserter(result) );
    return result;
}

The call it as:

    const auto concatenation = concatenate(first, second);

If the vectors are likely to be very large (or contain elements that are expensive to copy), then you might need to do a reserve first to save reallocations:

std::vector<int> concatenate(const std::vector<int>& lhs, const std::vector<int>& rhs)
{
    std::vector<int> result;
    result.reserve( lhs.size() + rhs.size() );
    std::copy( lhs.begin(), lhs.end(), std::back_inserter(result) );
    std::copy( rhs.begin(), rhs.end(), std::back_inserter(result) );
    return result;
}

(Personally, I would only bother if there was evidence it was a bottleneck).



回答4:

class Vector : public vector<int>
{
public:
    Vector operator+(const Vector& vec);
};

Vector Vector::operator+(const Vector& vec)
{
    for (int i = 0; i < vec.size(); i++)
    {
        this->push_back(vec[i]);
    }

    return *this;
}


回答5:

Let me preface this by saying this is a hack, and will not give an answer to how to do this using a vector. Instead we'll depend on sizeof(int) == sizeof(char32_t) and use a u32string to contain our data.

This answer makes it exceedingly clear that only primitives can be used in a basic_string, and that any primitive larger than 32-bits would require writing a custom char_traits, but for an int we can just use u32string.

The qualification for this can be validated by doing:

static_assert(sizeof(int) == sizeof(char32_t));

Once size equality has been established, and with the knowledge that things like non-const data, and emplace or emplace_back cannot be used, u32string can be used like a vector<int>, with the notable inclusion of an addition opperator:

const vector<int> first = {13};
const vector<int> second = {42};
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());

[Live Example]



回答6:

I came across this question looking for the same thing, and hoping there was an easier way than the one I came up with... seems like there isn't.

So, some iterator trickery should do it if you don't mind a helper template class:

#include <vector>
#include <iostream>

template<class T>
class concat
{
public:
    using value_type = typename std::vector<T>::const_iterator::value_type;
    using difference_type = typename std::vector<T>::const_iterator::difference_type;
    using reference = typename std::vector<T>::const_iterator::reference;
    using pointer = typename std::vector<T>::const_iterator::pointer;
    using iterator_category = std::forward_iterator_tag;
    concat(
        const std::vector<T>& first,
        const std::vector<T>& last,
        const typename std::vector<T>::const_iterator& iterator) : 
            mFirst{first},
            mLast{last},
            mIterator{iterator}{}
    bool operator!= ( const concat& i ) const
    {
        return mIterator != i.mIterator;
    }
    concat& operator++()
    {
        ++mIterator;
        if(mIterator==mFirst.end())
        {
            mIterator = mLast.begin();
        }
        return *this;
    }
    reference operator*() const
    {
        return *mIterator;
    }
private:
    const std::vector<T>& mFirst;
    const std::vector<T>& mLast;
    typename std::vector<T>::const_iterator mIterator;
};

int main()
{
    const std::vector<int> first{0,1,2,3,4};
    const std::vector<int> last{5,6,7,8,9};
    const std::vector<int> concatenated(
        concat<int>(first,last,first.begin()),
        concat<int>(first,last,last.end()));
    for(auto i: concatenated)
    {
        std::cout << i << std::endl;
    }
    return 0;
}

You may have to implement operator++(int) or operator== depending on how your STL implements the InputIterator constructor, this is the minimal iterator code example I could come up with for MingW GCC.

Have Fun! :)