Related
I'm having a problem encapsulating a vector. This is with pre C++ 11 code.
I have a class, let's call it A, that has a vector of objects as a member variable. I don't want to give direct access to vector to the clients of class A. However as a first stab I exposed the vector.
class A
{
public:
struct ConnectionEntry
{
int portNumber;
...
}
std::vector<ConnectionEntry> m_connectionList;
private:
}
There are parts of my code where I have to create vectors of class A and iterate through all of them. When I need to access all the elements of m_connectionList I end up with ugly looking code.
vector<A> vecOfA;
for (vector<A>::iterator it = vecOfA.begin; it != vecOfA.end(); it++)
{
for (vector<A::ConnectionEntry>::iterator conn = it->m_connectionList.begin();
conn != it->m_connectionList.end();
conn++)
{
}
}
I don't like that I have the vector exposed. I was thinking about implementing the operator[] and size() for class A and forwarding the values from m_connectionList but that doesn't seem clean to me.
Is there a standard way of solving this problem? Encapsulating the vector and only exposing certain parts without having to re-implement all the standard vector functions.
Personally, I would do the following:
class A
{
public:
struct ConnectionEntry
{
int portNumber;
...
}
typedef iterator typename std::vector<ConnectionEntry>::iterator;
typedef const_iterator typename std::vector<ConnectionEntry>::const_iterator;
// hope I got that one right, I am used to using
iterator begin() { return m_connectionList.begin(); }
iterator end() { return m_connectionList.end(); }
iterator cbegin() const { return m_connectionList.cbegin(); }
iterator cend() const { return m_connectionList.cend(); }
private:
std::vector<ConnectionEntry> m_connectionList;
}
And use it like this:
vector<A> vecOfA;
for (vector<A>::iterator it = vecOfA.begin; it != vecOfA.end(); it++)
{
for (A::iterator conn = it->begin(); conn != it->end(); conn++)
{
}
}
Btw will this be ready for ranged for loops when you are able to switch to C++11 in the future.
When using C++03, these are the possibilities:
#include <iostream>
#include <vector>
struct Foo {
struct Bar {
int value;
};
std::vector<Bar> bars;
};
int main() {
std::vector<Foo> foos;
for (unsigned int i = 0; i < foos.size(); ++i) {
for (unsigned int j = 0; j < foos[i].bars.size(); ++j) {
// do something
}
}
// or
typedef std::vector<Foo>::iterator FooIt;
typedef std::vector<Foo::Bar>::iterator BarIt;
for (FooIt foo = foos.begin(); foo != foos.end(); ++foo) {
for (BarIt bar = foo->bars.begin(); bar != foo->bars.end(); ++bar) {
// do something
}
}
return 0;
}
If you ever switch to C++11, you can use range-for loops:
std::vector<Foo> foos;
for (auto const& it : foos) {
for (auto const& bars : it.bars) {
// do something
}
}
I'm using multitreading and want to merge the results. For example:
std::vector<int> A;
std::vector<int> B;
std::vector<int> AB;
I want AB to have to contents of A and the contents of B in that order. What's the most efficient way of doing something like this?
AB.reserve( A.size() + B.size() ); // preallocate memory
AB.insert( AB.end(), A.begin(), A.end() );
AB.insert( AB.end(), B.begin(), B.end() );
This is precisely what the member function std::vector::insert is for
std::vector<int> AB = A;
AB.insert(AB.end(), B.begin(), B.end());
Depends on whether you really need to physically concatenate the two vectors or you want to give the appearance of concatenation of the sake of iteration. The boost::join function
http://www.boost.org/doc/libs/1_43_0/libs/range/doc/html/range/reference/utilities/join.html
will give you this.
std::vector<int> v0;
v0.push_back(1);
v0.push_back(2);
v0.push_back(3);
std::vector<int> v1;
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
...
BOOST_FOREACH(const int & i, boost::join(v0, v1)){
cout << i << endl;
}
should give you
1
2
3
4
5
6
Note boost::join does not copy the two vectors into a new container
but generates a pair of iterators (range) that cover the span of
both containers. There will be some performance overhead but maybe
less that copying all the data to a new container first.
In the direction of Bradgonesurfing's answer, many times one doesn't really need to concatenate two vectors (O(n)), but instead just work with them as if they were concatenated (O(1)). If this is your case, it can be done without the need of Boost libraries.
The trick is to create a vector proxy: a wrapper class which manipulates references to both vectors, externally seen as a single, contiguous one.
USAGE
std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };
VecProxy<int> AB(A, B); // ----> O(1). No copies performed.
for (size_t i = 0; i < AB.size(); ++i)
std::cout << AB[i] << " "; // 1 2 3 4 5 10 20 30
IMPLEMENTATION
template <class T>
class VecProxy {
private:
std::vector<T>& v1, v2;
public:
VecProxy(std::vector<T>& ref1, std::vector<T>& ref2) : v1(ref1), v2(ref2) {}
const T& operator[](const size_t& i) const;
const size_t size() const;
};
template <class T>
const T& VecProxy<T>::operator[](const size_t& i) const{
return (i < v1.size()) ? v1[i] : v2[i - v1.size()];
};
template <class T>
const size_t VecProxy<T>::size() const { return v1.size() + v2.size(); };
MAIN BENEFIT
It's O(1) (constant time) to create it, and with minimal extra memory allocation.
SOME STUFF TO CONSIDER
You should only go for it if you really know what you're doing when dealing with references. This solution is intended for the specific purpose of the question made, for which it works pretty well. To employ it in any other context may lead to unexpected behavior if you are not sure on how references work.
In this example, AB does not provide a non-const
access operator ([ ]). Feel free to include it, but keep in mind: since AB contains references, to assign it
values will also affect the original elements within A and/or B. Whether or not this is a
desirable feature, it's an application-specific question one should
carefully consider.
Any changes directly made to either A or B (like assigning values,
sorting, etc.) will also "modify" AB. This is not necessarily bad
(actually, it can be very handy: AB does never need to be explicitly
updated to keep itself synchronized to both A and B), but it's
certainly a behavior one must be aware of. Important exception: to resize A and/or B to sth bigger may lead these to be reallocated in memory (for the need of contiguous space), and this would in turn invalidate AB.
Because every access to an element is preceded by a test (namely, "i
< v1.size()"), VecProxy access time, although constant, is also
a bit slower than that of vectors.
This approach can be generalized to n vectors. I haven't tried, but
it shouldn't be a big deal.
Based on Kiril V. Lyadvinsky answer, I made a new version. This snippet use template and overloading. With it, you can write vector3 = vector1 + vector2 and vector4 += vector3. Hope it can help.
template <typename T>
std::vector<T> operator+(const std::vector<T> &A, const std::vector<T> &B)
{
std::vector<T> AB;
AB.reserve(A.size() + B.size()); // preallocate memory
AB.insert(AB.end(), A.begin(), A.end()); // add A;
AB.insert(AB.end(), B.begin(), B.end()); // add B;
return AB;
}
template <typename T>
std::vector<T> &operator+=(std::vector<T> &A, const std::vector<T> &B)
{
A.reserve(A.size() + B.size()); // preallocate memory without erase original data
A.insert(A.end(), B.begin(), B.end()); // add B;
return A; // here A could be named AB
}
One more simple variant which was not yet mentioned:
copy(A.begin(),A.end(),std::back_inserter(AB));
copy(B.begin(),B.end(),std::back_inserter(AB));
And using merge algorithm:
#include <algorithm>
#include <vector>
#include <iterator>
#include <iostream>
#include <sstream>
#include <string>
template<template<typename, typename...> class Container, class T>
std::string toString(const Container<T>& v)
{
std::stringstream ss;
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(ss, ""));
return ss.str();
};
int main()
{
std::vector<int> A(10);
std::vector<int> B(5); //zero filled
std::vector<int> AB(15);
std::for_each(A.begin(), A.end(),
[](int& f)->void
{
f = rand() % 100;
});
std::cout << "before merge: " << toString(A) << "\n";
std::cout << "before merge: " << toString(B) << "\n";
merge(B.begin(),B.end(), begin(A), end(A), AB.begin(), [](int&,int&)->bool {});
std::cout << "after merge: " << toString(AB) << "\n";
return 1;
}
All the solutions are correct, but I found it easier just write a function to implement this. like this:
template <class T1, class T2>
void ContainerInsert(T1 t1, T2 t2)
{
t1->insert(t1->end(), t2->begin(), t2->end());
}
That way you can avoid the temporary placement like this:
ContainerInsert(vec, GetSomeVector());
For this use case, if you know beforehand the number of results each thread produces, you could preallocate AB and pass a std::span to each thread. This way the concatenation need not be done. Example:
std::vector<int> AB(total_number_of_results, 0);
std::size_t chunk_length = …;
std::size_t chunk2_start = chunk_length;
std::size_t chunk3_start = 2 * chunk_length; // If needed
…
// Pass these to the worker threads.
std::span<int> A(AB.data(), chunk_length);
std::span<int> B(AB.data() + chunk2_start, chunk_length);
…
My answer is based on Mr.Ronald Souza's original solution. In addition to his original solution, I've written a vector proxy that supports iterators too!
short description for people who are not aware of the context of the original solution: the joined_vector template class (i.e the vector proxy)takes two references of two vectors as constructor arguments, it then treats them as one contiguous vector. My implementation also supports a forward-iterator.
USAGE:
int main()
{
std::vector<int> a1;
std::vector<int> a2;
joined_vector<std::vector<int>> jv(a1,a2);
for (int i = 0; i < 5; i++)
a1.push_back(i);
for (int i = 5; i <=10; i++)
a2.push_back(i);
for (auto e : jv)
std::cout << e<<"\n";
for (int i = 0; i < jv.size(); i++)
std::cout << jv[i] << "\n";
return 0;
}
IMPLEMENTATION:
template<typename _vec>
class joined_vector
{
_vec& m_vec1;
_vec& m_vec2;
public:
struct Iterator
{
typedef typename _vec::iterator::value_type type_value;
typedef typename _vec::iterator::value_type* pointer;
typedef typename _vec::iterator::value_type& reference;
typedef std::forward_iterator_tag iterator_category;
typedef std::ptrdiff_t difference_type;
_vec* m_vec1;
_vec* m_vec2;
Iterator(pointer ptr) :m_ptr(ptr)
{
}
Iterator operator++()
{
if (m_vec1->size() > 0 && m_ptr == &(*m_vec1)[m_vec1->size() - 1] && m_vec2->size() != 0)
m_ptr = &(*m_vec2)[0];
else
++m_ptr;
return m_ptr;
}
Iterator operator++(int)
{
pointer curr = m_ptr;
if (m_vec1->size() > 0 && m_ptr == &(*m_vec1)[m_vec1->size() - 1] && m_vec2->size() != 0)
m_ptr = &(*m_vec2)[0];
else
++m_ptr;
return curr;
}
reference operator *()
{
return *m_ptr;
}
pointer operator ->()
{
return m_ptr;
}
friend bool operator == (Iterator& itr1, Iterator& itr2)
{
return itr1.m_ptr == itr2.m_ptr;
}
friend bool operator != (Iterator& itr1, Iterator& itr2)
{
return itr1.m_ptr != itr2.m_ptr;
}
private:
pointer m_ptr;
};
joined_vector(_vec& vec1, _vec& vec2) :m_vec1(vec1), m_vec2(vec2)
{
}
Iterator begin()
{
//checkes if m_vec1 is empty and gets the first elemet's address,
//if it's empty then it get's the first address of the second vector m_vec2
//if both of them are empty then nullptr is returned as the first pointer
Iterator itr_beg((m_vec1.size() != 0) ? &m_vec1[0] : ((m_vec2.size() != 0) ? &m_vec2[0] : nullptr));
itr_beg.m_vec1 = &m_vec1;
itr_beg.m_vec2 = &m_vec2;
return itr_beg;
}
Iterator end()
{
//check if m_vec2 is empty and get the last address of that vector
//if the second vector is empty then the m_vec1's vector/the first vector's last element's address is taken
//if both of them are empty then a null pointer is returned as the end pointer
typename _vec::value_type* p = ((m_vec2.size() != 0) ? &m_vec2[m_vec2.size() - 1] : ((m_vec1.size()) != 0 ? &m_vec1[m_vec1.size() - 1] : nullptr));
Iterator itr_beg(p != nullptr ? p + 1 : nullptr);
itr_beg.m_vec1 = &m_vec1;
itr_beg.m_vec2 = &m_vec2;
return itr_beg;
}
typename _vec::value_type& operator [](int i)
{
if (i < m_vec1.size())
return m_vec1[i];
else
return m_vec2[i - m_vec1.size()];
}
size_t size()
{
return m_vec1.size() + m_vec2.size();
}
};
If your vectors are sorted*, check out set_union from <algorithm>.
set_union(A.begin(), A.end(), B.begin(), B.end(), AB.begin());
There's a more thorough example in the link.
Imagine the following scenario:
class A
{
int a[50];
int* GetAPtr() { return a; };
};
...
A b;
if(b.GetAPtr()[22] == SOME_RANDOM_DEFINE) do_this_and_that();
Is this kind of access considered bad practice? b.GetAPtr()[22]
To clarify my situation:
1. I cannot use new/malloc in this case, the array muste be static
2. This is meant to encapsulate older C code that uses multiple arrays where this comes extremly handy
3. I know that returning a pointer can possibly return a NULL pointer, we do not talk about that issue here
If you really need such const expression you could make it into a function:
class A
{
int a[50];
bool check_this_and_that() { return a[22] == SOME_RANDOM_DEFINE; };
};
...
A b;
if(b.check_this_and_that()) do_this_and_that();
magic numbers are bad in general but inside a class logic it's more forgiveable and outsiders don't have to see this.
Yes, it is bad practice, because you have no way of knowing how long the array is. You could follow the idiomatic standard library approach and return begin and end pointers, pointing to the first and one-past-last elements.
class A
{
int a[50];
int* begin() { return &a[0]; };
int* end() { return &a[50]; };
const int* begin() const { return &a[0]; };
const int* end() const { return &a[50]; };
size_t size() const { return 50; } // this could be handy too
};
As well as giving you the tools to iterate over the elements like you would over a standard library container, this allows you to check whether any pointer to an element of the array is < v.end(). For example
it* it = b.begin() + 22;
if(it < b.end() && *it == SOME_RANDOM_DEFINE) do_this_and_that();
This makes it trivial to use standard library algorithms:
A b;
// fill with increasing numbers
std::iota(b.begin(), b.end());
// sort in descending order
std::sort(s.begin(), s.end(), std::greater<int>());
// C++11 range based for loop
for (auto i : b)
std::cout << i << " ";
std::endl;
GetAPtr is a method for accessing a private data member. Now ask yourself what are the advantages of b.GetAPtr()[22] over b.a[22]?
Encapsulating data is a good way to maintain constraints on and between data members. In your case there is at least a correlation between the a array and its length 50.
Depending on the use of A you could build a interface providing different access patterns:
class A {
int a[50];
public:
// low level
int atA(unsigned i) const { return a[i]; }
// or "mid" level
int getA(unsigned i) const { if(i >= 50) throw OutOfRange(); return a[i]; };
// or high level
bool checkSomething() const { return a[22] == SOME_RANDOM_DEFINE; }
};
Consider the following:
struct A
{
int i;
double d;
std::string s;
};
std::list<A> list_A;
I'd like to copy all the elements of list_A to a map such that every pair in the map will consist of an element from list_A as value and its string s as key. Is there a way of doing it that is more elegant than looping through the list and insert each element along with its string as key to the map?
I love standard library algorithms and lambdas but it doesn't get much simpler than:
for (const A& value : list_A) {
map_A.insert(std::make_pair(value.s, value));
}
The other methods are doing the equivalent of this code and this loop is readable and just as fast.
This should get you the idea of how to use transform:
std::pair<std::string, A> pairify(const A& a) { return std::make_pair(a.s, a); }
std::transform(list.begin(), list.end(), std::inserter(map, map.end()), pairify);
The reason to use the inserter is:
An insert interator is a special type of output iterator designed to allow algorithms that usually overwrite elements (such as copy) to instead insert new elements automatically at a specific position in the container.
Sorry answered too quickly last time without details, here is a compilable code.
struct A
{
int i;
double d;
std::string s;
};
std::list<A> list_A;
std::pair<std::string, A> convert(const A &x) {
return make_pair(x.s,x);
}
int main() {
std::map<std::string,A> out;
std::transform(list_A.begin(), list_A.end(), std::inserter(out,out.end()),convert);
}
I may store it in a set: in this way there would not be data duplication in the map (s itself):
struct A
{
bool operator < (const A& r_) const { return (s < r_.s); }
int i;
double d;
std::string s;
};
std::list<A> list_A;
std::set<A> set_A;
for ( std::list<A>::const_iterator itr = list_A.begin(); itr != list_A.end(); ++itr ) {
if ( ! set_A.insert(*itr).second ) {
// Handle duplicated elements
}
}
I may keep the loop: in this way you could handle duplicated elements correctly.
If you use C++11 you can use lambda function with capture:
std::map<std::string, A> m;
std::list<A> l;
std::for_each(l.begin(), l.end(),
[&](const A& a) {
m.insert(std::make_pair(a.s, a));
});
I'm using multitreading and want to merge the results. For example:
std::vector<int> A;
std::vector<int> B;
std::vector<int> AB;
I want AB to have to contents of A and the contents of B in that order. What's the most efficient way of doing something like this?
AB.reserve( A.size() + B.size() ); // preallocate memory
AB.insert( AB.end(), A.begin(), A.end() );
AB.insert( AB.end(), B.begin(), B.end() );
This is precisely what the member function std::vector::insert is for
std::vector<int> AB = A;
AB.insert(AB.end(), B.begin(), B.end());
Depends on whether you really need to physically concatenate the two vectors or you want to give the appearance of concatenation of the sake of iteration. The boost::join function
http://www.boost.org/doc/libs/1_43_0/libs/range/doc/html/range/reference/utilities/join.html
will give you this.
std::vector<int> v0;
v0.push_back(1);
v0.push_back(2);
v0.push_back(3);
std::vector<int> v1;
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
...
BOOST_FOREACH(const int & i, boost::join(v0, v1)){
cout << i << endl;
}
should give you
1
2
3
4
5
6
Note boost::join does not copy the two vectors into a new container
but generates a pair of iterators (range) that cover the span of
both containers. There will be some performance overhead but maybe
less that copying all the data to a new container first.
In the direction of Bradgonesurfing's answer, many times one doesn't really need to concatenate two vectors (O(n)), but instead just work with them as if they were concatenated (O(1)). If this is your case, it can be done without the need of Boost libraries.
The trick is to create a vector proxy: a wrapper class which manipulates references to both vectors, externally seen as a single, contiguous one.
USAGE
std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };
VecProxy<int> AB(A, B); // ----> O(1). No copies performed.
for (size_t i = 0; i < AB.size(); ++i)
std::cout << AB[i] << " "; // 1 2 3 4 5 10 20 30
IMPLEMENTATION
template <class T>
class VecProxy {
private:
std::vector<T>& v1, v2;
public:
VecProxy(std::vector<T>& ref1, std::vector<T>& ref2) : v1(ref1), v2(ref2) {}
const T& operator[](const size_t& i) const;
const size_t size() const;
};
template <class T>
const T& VecProxy<T>::operator[](const size_t& i) const{
return (i < v1.size()) ? v1[i] : v2[i - v1.size()];
};
template <class T>
const size_t VecProxy<T>::size() const { return v1.size() + v2.size(); };
MAIN BENEFIT
It's O(1) (constant time) to create it, and with minimal extra memory allocation.
SOME STUFF TO CONSIDER
You should only go for it if you really know what you're doing when dealing with references. This solution is intended for the specific purpose of the question made, for which it works pretty well. To employ it in any other context may lead to unexpected behavior if you are not sure on how references work.
In this example, AB does not provide a non-const
access operator ([ ]). Feel free to include it, but keep in mind: since AB contains references, to assign it
values will also affect the original elements within A and/or B. Whether or not this is a
desirable feature, it's an application-specific question one should
carefully consider.
Any changes directly made to either A or B (like assigning values,
sorting, etc.) will also "modify" AB. This is not necessarily bad
(actually, it can be very handy: AB does never need to be explicitly
updated to keep itself synchronized to both A and B), but it's
certainly a behavior one must be aware of. Important exception: to resize A and/or B to sth bigger may lead these to be reallocated in memory (for the need of contiguous space), and this would in turn invalidate AB.
Because every access to an element is preceded by a test (namely, "i
< v1.size()"), VecProxy access time, although constant, is also
a bit slower than that of vectors.
This approach can be generalized to n vectors. I haven't tried, but
it shouldn't be a big deal.
Based on Kiril V. Lyadvinsky answer, I made a new version. This snippet use template and overloading. With it, you can write vector3 = vector1 + vector2 and vector4 += vector3. Hope it can help.
template <typename T>
std::vector<T> operator+(const std::vector<T> &A, const std::vector<T> &B)
{
std::vector<T> AB;
AB.reserve(A.size() + B.size()); // preallocate memory
AB.insert(AB.end(), A.begin(), A.end()); // add A;
AB.insert(AB.end(), B.begin(), B.end()); // add B;
return AB;
}
template <typename T>
std::vector<T> &operator+=(std::vector<T> &A, const std::vector<T> &B)
{
A.reserve(A.size() + B.size()); // preallocate memory without erase original data
A.insert(A.end(), B.begin(), B.end()); // add B;
return A; // here A could be named AB
}
One more simple variant which was not yet mentioned:
copy(A.begin(),A.end(),std::back_inserter(AB));
copy(B.begin(),B.end(),std::back_inserter(AB));
And using merge algorithm:
#include <algorithm>
#include <vector>
#include <iterator>
#include <iostream>
#include <sstream>
#include <string>
template<template<typename, typename...> class Container, class T>
std::string toString(const Container<T>& v)
{
std::stringstream ss;
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(ss, ""));
return ss.str();
};
int main()
{
std::vector<int> A(10);
std::vector<int> B(5); //zero filled
std::vector<int> AB(15);
std::for_each(A.begin(), A.end(),
[](int& f)->void
{
f = rand() % 100;
});
std::cout << "before merge: " << toString(A) << "\n";
std::cout << "before merge: " << toString(B) << "\n";
merge(B.begin(),B.end(), begin(A), end(A), AB.begin(), [](int&,int&)->bool {});
std::cout << "after merge: " << toString(AB) << "\n";
return 1;
}
All the solutions are correct, but I found it easier just write a function to implement this. like this:
template <class T1, class T2>
void ContainerInsert(T1 t1, T2 t2)
{
t1->insert(t1->end(), t2->begin(), t2->end());
}
That way you can avoid the temporary placement like this:
ContainerInsert(vec, GetSomeVector());
For this use case, if you know beforehand the number of results each thread produces, you could preallocate AB and pass a std::span to each thread. This way the concatenation need not be done. Example:
std::vector<int> AB(total_number_of_results, 0);
std::size_t chunk_length = …;
std::size_t chunk2_start = chunk_length;
std::size_t chunk3_start = 2 * chunk_length; // If needed
…
// Pass these to the worker threads.
std::span<int> A(AB.data(), chunk_length);
std::span<int> B(AB.data() + chunk2_start, chunk_length);
…
My answer is based on Mr.Ronald Souza's original solution. In addition to his original solution, I've written a vector proxy that supports iterators too!
short description for people who are not aware of the context of the original solution: the joined_vector template class (i.e the vector proxy)takes two references of two vectors as constructor arguments, it then treats them as one contiguous vector. My implementation also supports a forward-iterator.
USAGE:
int main()
{
std::vector<int> a1;
std::vector<int> a2;
joined_vector<std::vector<int>> jv(a1,a2);
for (int i = 0; i < 5; i++)
a1.push_back(i);
for (int i = 5; i <=10; i++)
a2.push_back(i);
for (auto e : jv)
std::cout << e<<"\n";
for (int i = 0; i < jv.size(); i++)
std::cout << jv[i] << "\n";
return 0;
}
IMPLEMENTATION:
template<typename _vec>
class joined_vector
{
_vec& m_vec1;
_vec& m_vec2;
public:
struct Iterator
{
typedef typename _vec::iterator::value_type type_value;
typedef typename _vec::iterator::value_type* pointer;
typedef typename _vec::iterator::value_type& reference;
typedef std::forward_iterator_tag iterator_category;
typedef std::ptrdiff_t difference_type;
_vec* m_vec1;
_vec* m_vec2;
Iterator(pointer ptr) :m_ptr(ptr)
{
}
Iterator operator++()
{
if (m_vec1->size() > 0 && m_ptr == &(*m_vec1)[m_vec1->size() - 1] && m_vec2->size() != 0)
m_ptr = &(*m_vec2)[0];
else
++m_ptr;
return m_ptr;
}
Iterator operator++(int)
{
pointer curr = m_ptr;
if (m_vec1->size() > 0 && m_ptr == &(*m_vec1)[m_vec1->size() - 1] && m_vec2->size() != 0)
m_ptr = &(*m_vec2)[0];
else
++m_ptr;
return curr;
}
reference operator *()
{
return *m_ptr;
}
pointer operator ->()
{
return m_ptr;
}
friend bool operator == (Iterator& itr1, Iterator& itr2)
{
return itr1.m_ptr == itr2.m_ptr;
}
friend bool operator != (Iterator& itr1, Iterator& itr2)
{
return itr1.m_ptr != itr2.m_ptr;
}
private:
pointer m_ptr;
};
joined_vector(_vec& vec1, _vec& vec2) :m_vec1(vec1), m_vec2(vec2)
{
}
Iterator begin()
{
//checkes if m_vec1 is empty and gets the first elemet's address,
//if it's empty then it get's the first address of the second vector m_vec2
//if both of them are empty then nullptr is returned as the first pointer
Iterator itr_beg((m_vec1.size() != 0) ? &m_vec1[0] : ((m_vec2.size() != 0) ? &m_vec2[0] : nullptr));
itr_beg.m_vec1 = &m_vec1;
itr_beg.m_vec2 = &m_vec2;
return itr_beg;
}
Iterator end()
{
//check if m_vec2 is empty and get the last address of that vector
//if the second vector is empty then the m_vec1's vector/the first vector's last element's address is taken
//if both of them are empty then a null pointer is returned as the end pointer
typename _vec::value_type* p = ((m_vec2.size() != 0) ? &m_vec2[m_vec2.size() - 1] : ((m_vec1.size()) != 0 ? &m_vec1[m_vec1.size() - 1] : nullptr));
Iterator itr_beg(p != nullptr ? p + 1 : nullptr);
itr_beg.m_vec1 = &m_vec1;
itr_beg.m_vec2 = &m_vec2;
return itr_beg;
}
typename _vec::value_type& operator [](int i)
{
if (i < m_vec1.size())
return m_vec1[i];
else
return m_vec2[i - m_vec1.size()];
}
size_t size()
{
return m_vec1.size() + m_vec2.size();
}
};
If your vectors are sorted*, check out set_union from <algorithm>.
set_union(A.begin(), A.end(), B.begin(), B.end(), AB.begin());
There's a more thorough example in the link.