Can't insert element into nested stl set of ints - c++

I have a nested set of ints but I cannot insert elements into the nested sets.
std::set<std::set<int> > centre_as_set = bitset_to_set(centre->second->bit_partitions);
std::set<std::set<int> >::iterator set_itr;
for ( set_itr = centre_as_set.begin(); set_itr != centre_as_set.end(); ++set_itr ) {
set_itr->insert(4);
std::set<int>::iterator node_itr;
for ( node_itr = set_itr->begin(); node_itr != set_itr->end(); ++node_itr ) {
std::cout << *node_itr;
}
}
}
The error is
Partition_standalone.cpp:612: error:
passing ‘const std::set, std::allocator >’
as ‘this’ argument of
‘std::pair, _Compare,
typename
_Alloc::rebind<_Key>::other>::const_iterator,
bool> std::set<_Key, _Compare,
_Alloc>::insert(const _Key&) [with _Key = int, _Compare = std::less, _Alloc =
std::allocator]’ discards
qualifiers
I can't quite decipher that template error, any help appreciated.

The elements in a set are not mutable and you are trying to use the non-const member function insert() on a const instance of std::set<int>. There is the following nice comment if you follow the iterator symbol to its declaration in stl_set.h:
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 103. set::iterator is required to be modifiable,
// but this allows modification of keys.
typedef typename _Rep_type::const_iterator iterator;
C++98 and C++03 allowed modification, but this is a defect and already fixed in non-ancient GCC versions and VC10. The mentioned defect report can be found here and will be incorporated into the next standard.
Use e.g. something like the following instead to add the value 4:
// Readability:
typedef std::set<int> IntSet;
typedef std::set<IntSet> IntSetSet;
// Helper:
IntSetSet add_value_to_sets(const IntSetSet& in, int i) {
IntSetSet ss;
IntSetSet::iterator set_itr;
for ( set_itr = in.begin(); set_itr != in.end(); ++set_itr ) {
IntSet s = *set_itr;
s.insert(4);
ss.insert(s);
}
return ss;
}
// ...
IntSetSet centre_as_set =
add_value_to_sets(bitset_to_set(centre->second->bit_partitions), 4);

Edit: This answer is wrong as per the comment of georg.
I dont have a compiler here, but the full declaration of std::set is:
template < class Key, class Compare = less<Key>,
class Allocator = allocator<Key> > class set;
The "Key" of the outermost set is "std::set". The Comparator is "std::less>", or short "operator<(set, set)" which is undefined. The compiler warns about this only on the first use/instantiation of the comparator.
I dont think there is a useful ordering/comparator for std::set's. You are better off using std::vector which doesnt order the elements and doesnt need a comparator.
Oh, and it is not allowed to change (at runtime) the set keys if this would affect the ordering. But that would be a runtime error, not a compile error.

Related

3d vectors - transform and minus

Can std::transform be used in a nested way? Tried to do this:
{
return std::transform(asciivec(inner.front()).begin(), asciivec(inner.front()).end(), asciivec(inner.back()).begin(), asciivec(inner.back()).end(),out.begin(), std::minus<float>{});
}
);
Error:
stl_algo.h||In instantiation of '_OIter std::transform(_IIter, _IIter, _OIter, _UnaryOperation) [with _IIter = __gnu_cxx::__normal_iterator<const std::vector<std::__cxx11::basic_string<char> >*, std::vector<std::vector<std::__cxx11::basic_string<char> > > >; _OIter = __gnu_cxx::__normal_iterator<double*, std::vector<double> >; _UnaryOperation = main()::<lambda(const auto:1&)>]':|
error: no matching function for call to 'transform(std::vector<double>::iterator, std::vector<double>::iterator, std::vector<double>::iterator, std::vector<double>::iterator, std::vector<double>::iterator, std::minus<float>)'|
You were on the right track, but you missed a few things.
For binary operations, std::transform only takes both begin and end iterators for the first input range; it takes a begin iterator for the second range, and assumes the second range is at least as large as the first (such that for any element in the range [first1, last1), there's a matching element in the range starting at first2).
Assuming that asciivec() returns a temporary object (and not a reference to a cached object), the iterators asciivec(inner.front()).begin() and asciivec(inner.front()).end() will actually refer to two different ranges; this will cause runtime errors. You'll need to cache the results of your asciivec() calls inside the lambda, to give them enough permanence to finish the operation.
std::minus evaluates its operands as lhs - rhs, and thus will return -1 for std::minus<float>{}('a', 'b'). As your example results are positive, I have assumed you want absolute values, and have expanded & commentated my code thusly.
So, considering that, we can make a few slight alterations...
// Renamed "inner" to "in" for convenience here.
std::transform (inp.cbegin(), inp.cend(), out.begin(), [&](const auto& in)
{
// Tying element type to asciivec() for convenience.
using Container = decltype(asciivec(in.front()));
using Elem = typename Container::value_type;
//using Elem = typename decltype(asciivec(in.front()))::value_type;
// Create non-temporary vectors first, to guarantee expected results.
std::vector<Container> inner = { asciivec(in.front()), asciivec(in.back()) };
// Use either...
// static Container ret;
// ret.clear(); ret.resize(inner.front().size());
// Or...
Container ret(inner.front().size());
std::transform(inner.front().begin(), // InputIt first1
inner.front().end(), // InputIt last1
inner.back().begin(), // InputIt first2
//asciivec(inner.back()).end(), // Omit
ret.begin(), // OutputIt d_first
std::minus<float>{}); // BinaryOperation binary_op
// I'm positive you want absolute values. ;3
// Lambda provides nice, clean overload resolution, compared to a function pointer or similar.
std::transform(ret.begin(), ret.end(), ret.begin(), [=](const Elem& e) { return std::fabs(e); });
return ret;
}
);
If signedness is, in fact, desired, we can comment out the second transform() call.
See it in action here.
Edited to increase genericity, it should be fine to just drop in regardless of your data's actual types.

Error with map and string conversion in c++

I'm familiar with Python's dictionary function, and I'm assuming based on my research that C++'s map function is relatively similar. However I'm running into a "conversion" issue:
std::string dictionary(std::string inquiry){
std::map<std::string, std::string> mapK;
mapK["12th st. Oakland City Center"]="12th";
mapK["16th st. Mission"]="16th";
return mapK.find(inquiry);
}
This is a string function that should receive a string and then with the corresponding key return a string back to the main. Evidently there's an issue with the return statement.
error: could not convert 'mapK.std::map<_Key, _Tp, _Compare, _Alloc>::find<std::basic_string<char>, std::basic_string<char>, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, std::basic_string<char> > > >((*(const key_type*)(& inquiry)))' from 'std::map<std::basic_string<char>, std::basic_string<char> >::iterator {aka std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, std::basic_string<char> > >}' to 'std::string {aka std::basic_string<char>}'
return mapK.find(inquiry);
std::map::find() returns iterator, specifically std::map::iterator.
The only thing you need to fix is to put a dereference:
return *mapK.find(inquiry);
Or even better, just use operator[]:
return mapK[inquiry];
Do note though that if inquiry is not in the map, it will return empty string. In case of find, it is straight undefined behavior.
Your problem is with the return type of find. It does not return the mapped type. Rather it returns an iterator.
The signature for find is iterator find( const Key& key ); (see here; there is also the const overload).
You should also check whether the key was found. So you could write something like
MapType::iterator i = mapK.find(inquiry);
if (i != mapK.end())
return i->second;
else
return ""; // or handle error some other way
Alternatively you could use the member access operator[], but this will insert a default constructed element in the map if the key is not found.

Remove duplicate vectors from vector of vectors

I am trying to implement the solution to the problem found at Link.
Here is my snippet of code
bool compareVec(vector<int> a, vector<int> b) {
return std::equal(a.begin(), a.end(), b.begin());
}
vector<vector<int> > ans;
ans.erase(std::remove_if(ans.begin(), ans.end(), compareVec), ans.end());
I am getting the following errors
/usr/include/c++/4.8/bits/stl_algo.h: In instantiation of
'_RandomAccessIterator std::__find_if(_RandomAccessIterator,
_RandomAccessIterator, _Predicate, std::random_access_iterator_tag) [with
_RandomAccessIterator = __gnu_cxx::__normal_iterator<std::vector<int>*,
std::vector<std::vector<int> > >; _Predicate = bool (*)(std::vector<int>,
std::vector<int>)]':
/usr/include/c++/4.8/bits/stl_algo.h:4465:41: required from '_IIter
std::find_if(_IIter, _IIter, _Predicate) [with _IIter =
__gnu_cxx::__normal_iterator<std::vector<int>*, std::vector<std::vector<int>
> >; _Predicate = bool (*)(std::vector<int>, std::vector<int>)]'
/usr/include/c++/4.8/bits/stl_algo.h:1144:64: required from '_FIter
std::remove_if(_FIter, _FIter, _Predicate) [with _FIter =
__gnu_cxx::__normal_iterator<std::vector<int>*, std::vector<std::vector<int>
> >; _Predicate = bool (*)(std::vector<int>, std::vector<int>)]'
solution.cpp:40:64: required from here
/usr/include/c++/4.8/bits/stl_algo.h:214:23: error: too few arguments to
function
if (__pred(*__first))
^
/usr/include/c++/4.8/bits/stl_algo.h:218:23: error: too few arguments to
function
if (__pred(*__first))
^
/usr/include/c++/4.8/bits/stl_algo.h:222:23: error: too few arguments to
function
if (__pred(*__first))
^
Can anyone help me out in debugging this?
Thanks in advance
EDIT
The elements of vector are sorted and all these vectors are also sorted.
Unique also gives an error. I am unable to figure out why?
Why is the example given in the link I provided, not helpful here?
std::remove_if requires a unary predicate. You pass a binary predicate, which causes your errors (/usr/include/c++/4.8/bits/stl_algo.h:222:23: error: too few arguments to function → your function takes two arguments, not one).
Further, std::remove_if does its removals with no consideration of other elements (which is why it accepts a unary predicate), so it isn't actually what you're looking for.
What you want to use is std::unique, which does require the compareVec you've implemented. However, std::vector already provides the operator== overload, so that implementation is actually redundant! Also, you say that you get an error when using std::unique. Try passing your parameters as const&.
Thus, when your outer vector and inner vectors are already sorted, the solution is as it'd be for any other vector of sorted elements:
outer.erase(std::unique(outer.begin(), outer.end()), outer.end());
Okay, since this is not marked with C++11, I will use a functor instead of a lambda.
The first problem you have is that remove_if takes a UnaryPredicate, which means it should only accept a single argument.
The second issue is also related to your understanding of remove_if. After you fix compareVec to only accept one argument, you're left wondering how you could possibly compare all elements against each other.
You could approach this one of two ways:
Sort your vector of vectors (< operator is defined lexicographically for vector) and then use std::unique (Examples) (More examples).
In the link you provided (same as the one I just linked to), notice that they sort first, and you do not.
Or, if there's no clear definition of < for your elements, only ==, you could perform an O(N2) lookup/erase on each subsequent item (shown below):
Comparison functor (could make as a lambda in C++11 and greater)
struct CompareVec
{
CompareVec(const std::vector<int>& _in) : compare_against(_in){}
bool operator()(const std::vector<int>& rhs) const
{
return compare_against == rhs;
};
const std::vector<int>& compare_against;
};
To be used like so:
for (size_t i = 0; i < ans.size(); ++i)
{
CompareVec comparator(ans[i]);
ans.erase(std::remove_if(ans.begin()+i+1, ans.end(), comparator), ans.end());
}
Live Demo (Compiled in C++11 for want of initializing test vectors with initializer lists)
Edit
In C++11 we can get rid of the CompareVec functor and replace it with a lambda:
for (size_t i = 0; i < ans.size(); ++i)
{
ans.erase(std::remove_if(ans.begin()+i+1, ans.end(),
[&ans, &i](const std::vector<int>& _rhs)
{
return ans[i] == _rhs;
}) , ans.end());
}
Demo2

How to check if a value exists within a C++ Map

I'm trying to use Dynamic Programming to implement fibonacci. Here's my .h file:
#ifndef DYNAMICPROGRAMMING_H
#define DYNAMICPROGRAMMING_H
#include <map>
class DynamicProgramming
{
public:
DynamicProgramming ();
~DynamicProgramming ();
int Fibonacci(int value);
private:
};
#endif // DYNAMICPROGRAMMING_H
Here's the relevant part in my .cpp file:
int DynamicProgramming::Fibonacci(int value)
{
int result;
std::map<int,int>fibonacci_storage;
std::map<int,int>::iterator valueFinder;
if (value == valueFinder->second){
return fibonacci_storage[value];
}
if (value <= 2 ){
result = 1;
} else {
result = Fibonacci(value - 1) + Fibonacci(value - 2);
}
fibonacci_storage.insert(std::pair<int,int>(value,result));
return result;
}
My error is coming from this line: if (value == valueFinder->second). This is what it says:
could not convert '((DynamicProgramming*)this)->DynamicProgramming::fibonacci_storage.std::map<_Key, _Tp, _Compare, _Alloc>::find [with _Key = int, _Tp = int, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, int> >, std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const int, int> >, std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]((*(const key_type*)(& value)))' from 'std::map<int, int>::iterator {aka std::_Rb_tree_iterator<std::pair<const int, int> >}' to 'bool'
It looks to me like this is a very simple error, but I'm not sure what all that stuff means. Can someone help me out, I'd really like to master this language, it seems like it would be very useful.
To check if a key exists in a C++ map, you can use std::map::count. It returns 0 (the key is absent) or 1 (the key is present).
To check if a value exists in a C++ map, I think that you have to iterate over all pairs.
It seems that your question is more about a compilation error though.
There are several issues in the code above.
Your iterator valueFinder is not iterating over anything. To iterate, you need to call the begin() method on a map.
The variable fibonacci_storage is basically useless because it is not a member of the object and thus there is a different instance of that variable for each call to the Fibonacci method.
Technically, you don't have to iterate because the indices of your Fibonacci sequence are the keys in the map, and the values of your Fibonacci sequence are the values in your map.
As far as I know the only way to search through maps values is to iterate through. If you are trying to see if the key exists then you can use
map.find()
There are several issues with your code.
The biggest one is not syntax, it's the placement of the cache: since fibonacci_storage is a local variable, each recursive invocation of Fibonacci will have its own cache, meaning that there would be no caching at all. You need to move fibonacci_storage into the private: section of your class.
As far as fixing the syntax goes, a common trick used to implement searching of cache is to store the result of [] in a reference, like this:
int DynamicProgramming::Fibonacci(int value)
{
int &result = fibonacci_storage[value];
if (result) {
return result;
}
if (value <= 2 ){
return (result = 1);
}
return (result = Fibonacci(value - 1) + Fibonacci(value - 2));
}
Variable result holds a reference to the value inside the map, so when you update it in one of the two assign-return statements the corresponding value inside the map gets updated automatically.
Demo.
valueFinder is just an iterator for the type std::map<int,int> that is not associated to any instance of that type.
To associate it to an instance (here fibonacci_storage) you have to assign it to that instance, i.e.
valueFinder = fibonacci_storage.begin();
Finding an element can be done with source
valueFinder = fibonacci_storage.find(value);
where value is the key you are searching for. Now you check if value is in the map:
if( valueFinder != fibonacci_storage.end() )
{
// value found
}
and you're done.

"Forced constness" in std::map<std::vector<int>,double> >?

Consider this program:
#include <map>
#include <vector>
typedef std::vector<int> IntVector;
typedef std::map<IntVector,double> Map;
void foo(Map& m,const IntVector& v)
{
Map::iterator i = m.find(v);
i->first.push_back(10);
};
int main()
{
Map m;
IntVector v(10,10);
foo(m,v);
return 0;
}
Using g++ 4.4.0, I get his compilation error:
test.cpp: In function 'void foo(Map&, const IntVector&)':
test.cpp:8: error: passing 'const std::vector<int, std::allocator<int> >' as 'this' argument of 'void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = int, _Alloc = std::allocator<int>]' discards qualifiers
I would expect this error if I was using Map::const_iterator inside foo but not using a non-const iterator.
What am I missing, why do I get this error?
The keys in a map are constant. A map is a tree, and you can't just going around changing the keys or you'll break its invariants. The value_type of a map with Key and Value is std::pair<const Key, Value>, to enforce this.
Your design needs some changing. If you really need to modify the key, you need to remove the element, change its key, and re-insert it with the new key.
Also concerning your example specifically, you will get undefined behavior (if this did work). Your map is empty when you call foo, so the iterator returned by find will be m.end(); the element doesn't exist. But then you'd go on to modify this non-existent element: ka-boom. Whenever you find something, you should check that it was found before you attempt to use it.