C++ maps, vectors, pairs with references - c++

I am trying to share references between a map and another vector as in the following code:
template <typename T>
void f(T& x) {
std::map<T, vector<int> > mp;
mp[x] = vector<int>(2, 4);
vector< pair<T&, int> > v1;
for (auto& kv : mp) {
v1.push_back( make_pair(kv.first, 0) );
}
}
and call this function from another
int x = 2;
f(x);
But this does not compile.
error: no matching function for call to ‘std::vector<std::pair<int&, int>, std::allocator<std::pair<int&, int> > >::push_back(std::pair<int, int>)’
v1.push_back( make_pair(kv.first, 0) );
Does anybody see why?
EDIT
I was able to make things work with
std::reference_wrapper
Here is my current solution.
template <typename T>
void f(T& x) {
std::map<std::reference_wrapper<T>, vector<int> > mp;
mp[x] = vector<int>(2, 4);
vector< pair<T&, int> > v1;
for (auto& kv : mp) {
v1.push_back( make_pair( std::ref(kv.first) , 0) );
}
v1[0].first = 34;
}
And
int x = 2;
f(x);
cout << x << endl;
prints 34. Do people see any cons with this vs the pointer based approach?

In general you can store std::pair<T&,int> in an std::vector, but you have to insert values correctly.
make_pair(x, 0) creates a std::pair<T,int> with no reference.
You can for example use an initializer lists instead:
v.push_back({x,0});
Another method would be using std::ref:
v.push_back(std::make_pair(std::ref(x),0));
Example:
std::vector<std::pair<int&,int>> v;
int a = 1;
v.push_back({a,a});
v.push_back(std::make_pair(std::ref(a),a));
a = 2;
std::cout << v[0].first << " " << v[0].second << std::endl; // will write "2 1"
However for your purpose it will not work. You should not get references to keys of a std::map. A std::map is a sorted container, you can not just change a key from the outside. This would mess up the inner workings of the std::map.

Because of type mismatch:
vector< pair<T&, int> > v1; // value_type is pair<int&, int>
// ^^^^^^^^^^^^^
and
v1.push_back( make_pair(kv.first, 0) );
// ^^^^^^^^^^^^^^^^^^^^^^ pair<int, int>
A lvalue-reference to int cannot be initialized with an rvalue.

You cannot have a container of references.
A reference must be initialized at the point of declaration, and declaring a container of references implies filling that container later on.
You could use pointers instead. Non (memory) owning pointers are considered valid c++ style.
Second option would be to look into
std::reference_wrapper

If you really want to reference the same object in your map and your vector of pairs, then you'd be better off looking at a pointer based solution. If your original type is stack based then your secondary collection could use a raw pointer - since it does not have ownership of the key object and so should not take any role in retaining or destructing it - to deference it. Although you'll have to be careful about managing the two collections in sync to avoid any dangling pointers.
That said, depending on why you want to do it, if it's a simple int it may not be worth the complexity.

Related

error: no matching function for call to 'std::vector<float>::push_back(std::vector<float>&) const'

I am trying to copy map (m) values into a vector (v)
The map is declared this way:
const map<int, vector<float> >& m = ....;
I tried this:
const vector<float>& v;
for(auto elem : m)
v.push_back(elem.second);
I get this error:
error: no matching function for call to 'std::vector::push_back(std::vector&) const'
The purpose of what you are doing is a little unclear but if you'd like to make a vector<float> to hold all the values in all the vectors in the map, you could do that, but you can't push_back a vector<float> directly, which is one of the reasons why you get the error:
error: no matching function for call to 'std::vector::push_back(std::vector&) const'
You can either push_back the float values one-by-one - or use the vector::insert member function to insert all of the elements in one go (which it looks like you are trying to do).
The other reason is that v is const which means that after it's been initialized, you can't make changes to it.
Some notes:
You've created both the map and the vector as const&s. This works and extends the lifetime of the temporary map and vector that you create this way - but being const you can't change any of them. I suggest that you remove the reference on at least the vector and also make it non-const to be able to fill it up.
Example:
#include <map>
#include <vector>
int main() {
const std::map<int, std::vector<float>> m { {1, {2,3,4}} };
std::vector<float> v;
for(auto& elem : m) {
// insert the vector values from this map-entry at the end of `v`
v.insert(v.end(), elem.second.begin(), elem.second.end());
}
}
Note auto& elem instead of auto elem. The latter copies the data (which may be expensive) while auto& elem takes it by reference.
Using structured bindings, the filling loop would look like this:
for(auto&[first, second] : m) {
v.insert(v.end(), second.begin(), second.end());
}
You're trying to push a vector to a vector, which doesn't make sense. An object can't be an element of itself.
Change it to:
const map<int, float>& m = ....;
Or
for(auto elem : m)
{
int i = 0;
while(i < elem.second.size())
{
v.push_back(elem.second[i]);
i += 1;
}
}
your map type is: int, vector<float>
it means your values are type of vector of floats.
your vector have float type.
you can't push_back vector of float in one element of your vector.
you need to do this.
vector<float> v;
for(auto elem : m)
{
for(auto f : elem.second)
v.push_back(f);
}
or something like that depends on how you want to use your values.
you try to push VECTOR to vector u need Vector of Vector for this to work
your element is vector and variable v is a vector u need vector<vector>

C++ Copy a vector of pair<int,int> to a vector<int>

I've a vector of pair which I need to copy them linearly to a vector of ints. I've the following code which works well, but I'm not sure if it's safe considering struct padding issues in C++.
std::vector < std::pair<int, int> > test_vector;
for (int i=0;i<5;i++) {
test_vector.push_back(std::make_pair(i,i*5));
}
std::vector<int> int_vec(test_vector.size() * 2);
std::copy(reinterpret_cast<int*>(&(*test_vector.begin())),reinterpret_cast<int*>(&(*test_vector.end())),int_vec.begin());
Now, my question is - Is the above code safe? If not, is there an elegant way to do it without writing a loop?
How about std::transform and a lambda function ?
std::vector<int> v;
std::transform(test_vector.begin(), test_vector.end(), std::back_inserter(v),
[&v](const std::pair<int, int> &p)
{ v.push_back( p.first);
return p.second ;});
If you can't use C++11, and probably "hate" doing linear copying using loops
You can use functor like:
struct X{
X(std::vector<int> &x) :v(x){}
int operator () (const std::pair<int, int> &p)
{
v.push_back(p.first);
return p.second;
}
std::vector<int> &v;
};
std::vector<int> v; //Final vector
std::transform(test_vector.begin(),
test_vector.end(),
std::back_inserter(v),
X(v));
std::vector<int> ::iterator it;
for(it=v.begin() ; it!=v.end() ;++it)
std::cout<<*it<<" ";
You don't need any fanciness for this problem. A simple for loop will do, especially if you can't use C++11
std::vector < std::pair<int, int> > test_vector;
std::vector<int> int_vec; int_vec.reserve(test_vector.size() * 2);
for (std::vector < std::pair<int, int> >::const_iterator it = test_vector.begin(), end_it = test_vector.end(); it != end_it; ++it)
{
int_vec.push_back(it->first);
int_vec.push_back(it->second);
}
You're right to be concerned about structure padding issues, but I think you haven't really faced the central assumption that your code is making:
Can I treat a std::pair<int, int> as an array of two integers, with the .first being the first element in the array and .second being the second element?
From a "correctness" point of view, I'd say "no". You've identified padding issues, but there's also the ordering of the fields. There's really no guarantee that .first has a lower memory address than .second.
From a "practical" point of view, I'd be quite surprised your that code did not work. [ Edit: Neil has pointed out a concrete example where there are padding issues; so color me surprised. Besides being "bad form", I now consider the code broken in practice. ]
As for a solution, you can use for_each with a custom action that pushes both elements of the pair (untested code)
struct action {
action ( vector<int> & target ) : t_(target) {}
void operator () ( const pair &p ) const
{ t_.push_back(p.first); t_.push_back(p.second); }
private:
vector<int> &t_;
}
for_each ( test_vector.begin(), test_vector.end(), action(v));
A reinterpret_cast is usually bad news. Wouldn't you be better off reserve()ing enough space in the destination vector and then calling std::for_each on the source vector of pairs and then have the function/lambda push_back both first and second into the destination vector ?

How to initilaize vector of pairs with NULL (c++)

I have a vector of pairs, which is empty at the beginning. I implemented a custom insert and remove method, and I would also like to be able to assign NULL to certain elements, but I can't because it's not a pointer to pair.
If I try to illustrate it more specifically - given a vector V
std::vector< std::pair<A,B> > V;
neither
V.assign(number,NULL);
nor
V[n]=NULL;
would work.
I need to do this to check if there already is an element saved in a certain slot. Is there anywork around or should I just create another vector of booleans to save wheter a certian slot is full or not?
NOTE: I know any kind of map would solve it elegantly, but it has to be a vector.
I think solution with map would be optimal in your case:
std::map<int, std::pair<A, B> > M;
Then you can do
M.erase(M.find(number))
To NULL-ify to the element.
If I had to do it I would do something like
vector< pair< pair<A,B> ,int > >V , V[i].second can be 0 or 1 depending whether the element
pair has to be NULL or not.This if if you want to mark the pair NULL but still keep it for refernece.Otherwise use map as Alex1985 said.
I advise you to look at boost::optional
boost::optional< std::vector< std::pair<A,B> > > V;
if (V) {
// V was initialized
} else {
// wasn't initialized
}
documentation examples: http://www.boost.org/doc/libs/1_54_0/libs/optional/doc/html/boost_optional/examples.html
Use shared_ptr as the second type in the pair.
std::vector< std::pair<A,std::shared_ptr<B> > > V;
V.assign(number, std::shared_ptr<B>());
V[n] = std::shared_ptr<B>();
V[n] = std::shared_ptr<B>(new B());
if (V[n].get() == NULL) { /* process empty here */ }
The shared_ptr class was introduced to C++ in TR1, and is part of the C++11 specification. It works well with standard containers because it

trying to reflect the effect a change to the original variable (call by reference)

I have defined a vector which consist of pairs and vectors. For better readability, I do this
#include <iostream>
#include <vector>
using namespace std;
class foo {
public:
vector< pair<int, vector<int> > > v;
vector< pair<int, vector<int> > >::iterator it;
void bar()
{
for( it = v.begin(); it != v.end(); it++ ) {
if ( 10 == (*it).first ) {
vector<int> a = (*it).second; // NOTE
a[0] = a[0]*2; // NOTE
}
}
}
};
int main()
{
foo f;
f.bar();
return 0;
}
As you can see, I assign (*it).second to a variable a so that I manipulate easily. Problem is, when I change the value of a, the original vector v doesn't change. I can resolve that changing a to (*it).second every where in the loop. However this will make the code hard for reading.
Is there any way to reflect the changes in a to the original v? I have to say call by reference like this
vector<int> a = &(*it).second;
doesn't work
You need to declare a as a reference:
vector<int>& a = (*it).second;
Ampersand in std::vector<int> a = &(*it).second; is being parsed as adress-of operator and a isn't a pointer - that's why it doesn't work/compile.
You can change the value of the original vector only when you take it into a reference.
But instead in your code you are creating a copy of it and changing that copy which does not effect the original vector.
your vector<int> a should be a reference instead.
vector<int> &a...
first of all: there is no code which demonstrates how do you fill the v member and when you initialize it. just make sure that you have a valid it after all (remember that, all iterators into vector may invalidate after v modifications)
and the second: the correct line should be like this
vector<int>& a = it->second;
(you take an address instead, and your snippet even wouldn't compile)

Search in vector<std::pair<int, vector<int> > >

I would like to search within a vector<std::pair<int, vector<int> > >. This won't work due to the vector parameters:
std::vector<std::pair<int, std::vector<int> > > myVec;
iterator = find(myVec.begin(), myVec.end(), i);
Search would be on the first std::pair template parameter (int), in order to get the vector associated with it.
std::vector<std::pair<int, std::vector<int> > > myVec;
This requires C++0x for the lambda expression:
typedef std::pair<int, std::vector<int>> pair_type
std::find_if(myVec.begin(), myVec.end(), [i](pair_type const& pair)
{ return pair.first == i; });
If you're not using C++0x then either roll out your own loop or use something like Boost.Phoenix/Boost.Lambda.
Or, for both cases, why not use std::map?
You could make do with the following (pretty ugly) functoid:
struct FindFirst {
FindFirst(int i) : toFind(i) { }
int toFind;
bool operator()
( const std::pair<int, std::vector<int> > &p ) {
return p.first==toFind;
}
};
using it like this ( I couldn't get bind2nd to work - that's why I used the c'tor ):
int valueToFind = 4;
std::find_if(myVec.begin(), myVec.end(), FindFirst(valueToFind));
I think what you would like is a map:
std::map< int, vector< int > > foo;
You can then add elements, search by key etc:
int key = 4; //This will be the key
vector<int> value(5, 4); //Fill some values (5 4's in this case) into the vector
foo[key]=value; //Adds the pair to the map. Alternatively;
foo.insert( make_pair(key, value) ); //Does the same thing (in this context)
Looking at the way you've done things though, you might be wanting a std::multimap (which allows multiple values to have the same key) Class docs here
You're trying to map an int to a vector of int.
So try map<int, vector<int> >.
The second template parameter of a vector is the allocator - your compiler can probably puzzle out what you wanted to say, the declaration is wrong anyway. What you probably want is some sort of map type, like iammilind suggested.