11

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()); 
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • literally first google result http://stackoverflow.com/questions/201718/concatenating-two-stdvectors – RyanP Feb 18 '16 at 15:45
  • @RyanP Did I miss something in there that answered my question? Or perhaps I should assume that the answer to my question is no, since there wasn't anything in there that answered it? – Jonathan Mee Feb 18 '16 at 15:48
  • If you can't use methods that modify the container and you don't want to use a helper method and there is no addition operator for vector then I'm afraid the answer to your question is no. – Mohamad Elghawi Feb 18 '16 at 15:50
  • @JonathanMee I agree that it doesn't have the specific answer you are looking for and that maybe it was missed. But with 225 upvotes on the question, over 100k views, and that many answers personally I'd find it unlikely that if the solution you want existed it wouldn't have been mentioned, given that it would be the preferred answer to all of the answers that were given. In this case. – RyanP Feb 18 '16 at 15:52
  • @RyanP I mean this is certainly possible using `basic_string` but I'm not sure of the ramifications of such a misuse of `string`, but suffice to say it is possible. – Jonathan Mee Feb 18 '16 at 15:59
  • @JonathanMee it is possible using `basic_string`, but the question is about `std::vector` – RyanP Feb 18 '16 at 16:03
  • @RyanP I'm curious now, if I'm just using this as a container, I wonder what the drawbacks of using `basic_string` are? – Jonathan Mee Feb 18 '16 at 16:08
  • 1
    @JonathanMee some of them http://stackoverflow.com/a/4557156/4525052 – RyanP Feb 18 '16 at 17:13
  • @RyanP it looks like other than a hint to the compiler all the differences were removed by C++11. I'm going to run a couple tests and see if this is viable. – Jonathan Mee Feb 18 '16 at 17:44
  • @RyanP So after pretty thorough testing I'm certain this can be done, for `int`s and other smaller primitives only. You're right that it doesn't accomplish this using `vector` which I note in [the answer](http://stackoverflow.com/a/35555016/2642059), so I'm not going to accept it, but I thought it might be useful information for someone other than me, so I've posted it here. – Jonathan Mee Feb 22 '16 at 13:36

6 Answers6

9

No, it's not possible if you require that

  • no helper function is defined, and
  • the resulting vector can be declared const.
Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • 5
    I'm somewhat saddened to see that of all the answers I gave, it's this negative one which got me my gold C++ badge. :o[ – Frerich Raabe Feb 18 '16 at 16:00
  • @Reader I'm accepting this, but there is a hack that can circumnavigate this problem available here: http://stackoverflow.com/a/35555016/2642059 – Jonathan Mee Feb 23 '16 at 13:39
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;
Matt Jordan
  • 2,133
  • 9
  • 10
  • 3
    Not very optimal just like the other answer. This is going to cause at least one reallocation which can be expensive. – NathanOliver Feb 18 '16 at 15:56
  • @NathanOliver: "This is going to cause at least one reallocation" - That depends on your implementation. If first and second are less than the minimum vector size, then no it won't. – Martin Bonner supports Monica Feb 18 '16 at 15:58
  • Optimal wasn't a requirement; the requirement was to allow concatenating two vectors into a const vector. If avoiding re-allocations is a requirement, then the operator overload can be extended to initialize the movable return value with a reserve size of v1.size() + v2.size(). – Matt Jordan Feb 18 '16 at 15:59
  • @MartinBonner I have to say I'm a little stunned that copying code from the question and blatantly disregarding the requirements of the question earns an upvote. – Jonathan Mee Feb 18 '16 at 16:06
1

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).

0
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;
}
user2081740
  • 81
  • 1
  • 3
  • 9
  • This not only *does* require writing an additional function, but it also requires me to create and use a non-standard class, and it exhibits a far more virulent version of the reallocation problem than does @MartinBonner 's answer: http://stackoverflow.com/questions/35485866/vector-construction-from-the-concatenation-of-2-vectors?noredirect=1#comment58665572_35486013 – Jonathan Mee Feb 22 '16 at 12:01
0

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]

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
0

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! :)