const arguments in std::remove_if - c++

I'm going to remove elements from a list of pairs. When I'm using a pair like
std::pair<const int, bool>
I get the following compilation error:
In file included from /usr/local/include/c++/6.1.0/utility:70:0,
from /usr/local/include/c++/6.1.0/algorithm:60,
from main.cpp:1:
/usr/local/include/c++/6.1.0/bits/stl_pair.h: In instantiation of
'std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_T1,
_T2>&&) [with _T1 = const int; _T2 = bool]':
/usr/local/include/c++/6.1.0/bits/stl_algo.h:868:16: required from
'_ForwardIterator std::__remove_if(_ForwardIterator, _ForwardIterator,
_Predicate) [with _ForwardIterator = std::_List_iterator > _Predicate =
__gnu_cxx::__ops::_Iter_pred&)> >]'
/usr/local/include/c++/6.1.0/bits/stl_algo.h:936:30: required from
'_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter =
std::_List_iterator > _Predicate =
main()::&)>]'
main.cpp:17:32: required from here
/usr/local/include/c++/6.1.0/bits/stl_pair.h:319:8: error: assignment
of read-only member 'std::pair::first'
first = std::forward(__p.first);
This is the sample code:
int main()
{
int id = 2;
std::list< std::pair <const int, bool> > l;
l.push_back(std::make_pair(3,true));
l.push_back(std::make_pair(2,false));
l.push_back(std::make_pair(1,true));
l.erase(std::remove_if(l.begin(), l.end(),
[id](std::pair<const int, bool>& e) -> bool {
return e.first == id; }));
for (auto i: l) {
std::cout << i.first << " " << i.second << std::endl;
}
}
I know that (please correct me If I am wrong):
I will have exactly the same problem as long as there is constness in any element of the list, for example, a list <const int> will also return a compilation error.
If I remove the const in the first element of the pair the code will work.
The more elegant and efficient way to do it is by using the remove_if list method, like this:
l.remove_if([id](std::pair<const int, bool>& e) -> bool {
return e.first == id; });
but my question is, what are exactly the inner workings of std::remove_if that impose the elements of the container not being const?

The general std::remove_if shuffles item values around to put the logically erased values at the end of the sequence (it's typically used in combination with member function erase to actually remove the logically erased values). It can't do that shuffling when an item isn't copyable or movable. Instead use std::list::remove_if.

If you look at the type and iterator requirements of std::remove_if, you can see that the implementation must be similar to the following (from the link above):
template<class ForwardIt, class UnaryPredicate>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
first = std::find_if(first, last, p);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!p(*i))
*first++ = std::move(*i);
return first;
}
I.e., the algorithm assumes only that the iterators have forward capabilities, and elements are moveable, and it moves elements around. Of course, moves can't be done on const objects.

Related

Why is my variable constant when it wasn't when I defined it?

Currently, I have a problem when trying to find a variable within a vector.
I defined a vector, tests, set to contain my own structure Test. It contains two variables, one and two. Both are instances of Test.
Test isn't much, it only contains an int and char.
I defined a macro that finds wether or not an instance of an object is in a given vector or not.
When I attempt to compile my code, it results in this error:
In file included from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algobase.h:71,
from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/char_traits.h:39,
from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/ios:40,
from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/ostream:38,
from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/iostream:39,
from test.cpp:1:
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/predefined_ops.h: In instantiation of 'bool __gnu_cxx::__ops::_Iter_equals_val<_Value>::operator()(_Iterator) [with _Iterator = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Value = const Test]':
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algobase.h:1932:14: required from '_RandomAccessIterator std::__find_if(_RandomAccessIterator, _RandomAccessIterator, _Predicate, std::random_access_iterator_tag) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<const Test>]'
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algobase.h:1977:23: required from '_Iterator std::__find_if(_Iterator, _Iterator, _Predicate) [with _Iterator = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<const Test>]'
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algo.h:3902:28: required from '_IIter std::find(_IIter, _IIter, const _Tp&) [with _IIter = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Tp = Test]'
test.cpp:19:6: required from here
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/predefined_ops.h:268:17: error: no match for 'operator==' (operand types are 'Test' and 'const Test')
268 | { return *__it == _M_value; }
|
What I find interesting is that the variable I am checking is considered constant, even though when I defined it, it wasn't.
Source code:
#include <iostream>
#include <vector>
#include <algorithm>
#define isin(one, two) (std::find(one.begin(), one.end(), two) != one.end())
struct Test {
int one;
char two;
};
int main() {
Test one = { 6, 'n' };
Test two = { 9, 'r' };
std::vector<Test> tests = { one, two };
if (isin(tests, one)) {
std::cout << "one is in tests\n";
} else {
std::cout << "one is not in tests\n";
}
return 0;
}
Why is one considered constant? Did I accidentally do something wrong?
error: no match for 'operator==' (operand types are 'Test' and 'const Test')
You don't have an equality operator defined for Test. You need to define operator== so it can know how to check if two instances are equal.
The error message is telling you that there is no operator== defined that can compare a non-const Test object on the left to a const Test object on the right. The compiler does not generate a default operator== for you (though, this is actually addressed in C++20, though it is not automatic, you still have to request it explicitly).
The 2nd Test in the error message is const because that is how std::find() is taking it in:
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value )
std::find() iterates the specified range comparing value to each element in the range using operator==, similar to this:
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value )
{
while (first != last) {
if (*first == value) { // <-- here
return first;
}
++first;
}
return last;
}
In this case, T is Test, so value is const Test &value. tests is a non-const std::vector<Test>, so its iterators refer to non-const Test elements. But the value being compared to them is a const Test.
You need to define an operator== for Test that can handle that comparison, eg:
struct Test {
int one;
char two;
bool operator==(const Test &rhs) const {
return one == rhs.one && two == rhs.two;
/* alternatively:
return std::tie(one, two) == std::tie(rhs.one, rhs.two);
*/
}
/* alternatively in C++20 onwards:
bool operator==(const Test &) const = default;
*/
};

Own vector assign implementation

I'm implementing stl like vector with writing all default functions. And there is a problem that I don't understand why It calls ragne version of assign for simple types and doesn't default.
Here is the implementation code:
Vector.h
void assign(size_t count, const T& value){ // Default version
void assign(size_t count, const T& value){
if(this->_size < count){
this->allocator.deallocate(this->arr, this->_capacity);
this->arr = this->allocator.allocate(count);
this->_capacity = count;
}
for(size_t i = 0; i < count; ++i)
this->arr[i] = value;
this->_size = count;
}
template<class InputIt>
void assign(InputIt first, InputIt last){ // Range version
size_t count = std::distance(first,last);
if(this->_size < count){
this->allocator.deallocate(this->arr, this->_capacity);
this->arr = this->allocator.allocate(count);
this->_capacity = count;
}
for(size_t i = 0; first != last; i++)
this->arr[i] = *first++;
this->_size = count;
}
Main code:
Vector<int> vec;
vec.assign(5,10);
Output:
/MyVector/MyVector.h: In instantiation of ‘void Vector<T, Allocator>::assign(InputIt, InputIt) [with InputIt = int; T = int; Allocator = std::allocator]’:
../MyVector/main.cpp:52:24: required from here
../MyVector/MyVector.h:99:45: error: no matching function for call to ‘distance(int&, int&)’
size_t count = std::distance(first,last);
~~~~~~~~~~~~~^~~~~~~~~~~~
In file included from /usr/include/c++/7/bits/stl_algobase.h:66:0,
from /usr/include/c++/7/bits/char_traits.h:39,
from /usr/include/c++/7/ios:40,
from /usr/include/c++/7/ostream:38,
from /usr/include/c++/7/iostream:39,
from ../MyVector/main.cpp:1:
/usr/include/c++/7/bits/stl_iterator_base_funcs.h:138:5: note: candidate: template<class _InputIterator> constexpr typename std::iterator_traits<_Iterator>::difference_type std::distance(_InputIterator, _InputIterator)
distance(_InputIterator __first, _InputIterator __last)
^~~~~~~~
/usr/include/c++/7/bits/stl_iterator_base_funcs.h:138:5: note: template argument deduction/substitution failed:
/usr/include/c++/7/bits/stl_iterator_base_funcs.h: In substitution of ‘template<class _InputIterator> constexpr typename std::iterator_traits<_Iterator>::difference_type std::distance(_InputIterator, _InputIterator) [with _InputIterator = int]’:
../MyVector/MyVector.h:99:45: required from ‘void Vector<T, Allocator>::assign(InputIt, InputIt) [with InputIt = int; T = int; Allocator = std::allocator]’
../MyVector/main.cpp:52:24: required from here
/usr/include/c++/7/bits/stl_iterator_base_funcs.h:138:5: error: no type named ‘difference_type’ in ‘struct std::iterator_traits<int>’
In file included from ../MyVector/main.cpp:2:0:
../MyVector/MyVector.h: In instantiation of ‘void Vector<T, Allocator>::assign(InputIt, InputIt) [with InputIt = int; T = int; Allocator = std::allocator]’:
../MyVector/main.cpp:52:24: required from here
../MyVector/MyVector.h:107:36: error: invalid type argument of unary ‘*’ (have ‘int’)
this->arr[i] = *first++;
^~~~~~~~
Makefile:725: recipe for target 'main.o' failed
make: *** [main.o] Error 1
I'm using C++17
The range version is a better match for vec.assign(5, 10); with InputIt = int. You should somehow disable that overload for a template parameter that doesn't represent an input iterator.
Let's take a look at stdlibc++ implementation:
template<typename InputIt, typename = std::RequireInputIter<InputIt>>
void assign(InputIt first, InputIt last) {
M_assign_dispatch(first, last);
}
where RequireInputIter is
template<typename InputIt>
using RequireInputIter = typename enable_if<is_convertible<typename
iterator_traits<InputIt>::iterator_category, input_iterator_tag>::value>::type;
In other words, for a deduced type InputIt, iterator_traits<InputIt>::iterator_category type should be convertible into input_iterator_tag. Otherwise, that assign overload is silently excluded from the overload resolution set thanks to SFINAE.
In C++17, RequireInputIter can be simplified with _t and _v helpers:
template<typename InputIt>
using RequireInputIter = enable_if_t<is_convertible_v<typename
iterator_traits<InputIt>::iterator_category, input_iterator_tag>>;
Also note that input iterators can be used to traverse a range only once. After you call std::distance(first, last), all subsequent attempts to traverse the range are undefined behaviour unless InputIt is at least a forward iterator. For input iterators you can't determine how much space to preallocate.
That's why assign uses tag dispatch technique internally. With some simplifications it looks like this:
template<typename InputIt, typename = std::RequireInputIter<InputIt>>
void assign(InputIt first, InputIt last) {
M_assign_aux(first, last,
typename iterator_traits<InputIt>::iterator_category{});
}
There are two M_assign_aux overloads
template<typename InputIt>
void M_assign_aux(InputIt first, InputIt last, std::input_iterator_tag);
template<typename ForwardIt>
void M_assign_aux(ForwardIt first, ForwardIt last, std::forward_iterator_tag);
to do the assignment. The first one will be used for input iterators only, and the second one - for forward iterators and those derived from it, i.e. bidirectional and random access ones.
You can call a specific function using cast. Like:
assign( (size_t) 5, (const int&) 10);

Removing elements of 2D vector

I have a 2D vector and I need to remove blocks that do not contain sufficient values:
typedef vector<double> iniMatrix;
bool hasInsufficientEnergy() {
return true;
}
vector<double> Audio::filter(vector<iniMatrix>&blocks, double sumThres, double ZeroThres)
{
vector<double> totalEnergy;
vector<double> totalZeroCross;
double totalSum = sumThres * blocks.size();
double totalZero = ZeroThres * blocks.size();
vector<iniMatrix> blockked;
for(unsigned i=0; (i < blocks.size()); i++)
{
totalEnergy.push_back(abs(this->energy(blocks[i])));
totalZeroCross.push_back(zerocross(blocks[i]));
if(!totalEnergy[i] > totalSum || totalZeroCross[i] < ZeroThres)
{
hasInsufficientEnergy();
}else{
//hasInsufficientEnergy();
}
iniMatrix::iterator erase_after = remove_if(blocks[i].begin(), blocks[i].end(),
&hasInsufficientEnergy);
}
}
The problem is erase_after and comes up with an error message:
In function ‘_OutputIterator std::remove_copy_if(_InputIterator, _InputIterator,
_OutputIterator, _Predicate) [with _InputIterator = __gnu_cxx::__normal_iterator<double*,
std::vector<double, std::allocator<double> > >, _OutputIterator =
__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >,
_Predicate = bool]’:
/usr/include/c++/4.2.1/bits/stl_algo.h:1302: instantiated from ‘_ForwardIterator
std::remove_if(_ForwardIterator, _ForwardIterator, _Predicate) [with _ForwardIterator =
__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >,
_Predicate = bool]’
Audio.cpp:105: instantiated from here
/usr/include/c++/4.2.1/bits/stl_algo.h:1227: error: ‘__pred’ cannot be used as a function
Anyone have any ideas to where I am going wrong?
The third argument to std::remove_if is a predicate function (or any callable entity, for that matter) that takes a single element and returns true if it needs to be removed and false if not. The element of a vector of vectors (or "2D vector", as you call it) is a vector, vector<double> in your case. That's what the argument to your predicate should be:
bool HasInsufficientEnergy(const vector<double>& elem)
{
// herein is the code that tests elem and decides
// whether it should be removed or not
}
Then your filter() method shouldn't contain much more than this
void Audio::filter(vector<iniMatrix>&blocks)
{
auto it = std::remove_if(blocks.begin(), blocks.end(), &HasInsufficientEnergy);
blocks.erase(it, blocks.end());
}
If your predicate needs aditional arguments, then implement it as a class that takes them as constructor parameters. You need to overload operator(), too, so you have a callable functor.
If your compiler supports C++11, then learn how to use lambda functions as they are very useful for exactly this task.
true or false aren't function objects. If you want to use remove_if you must give it a pointer to function or function object, which takes a member of the collection as parameter and returns whether to remove it or not. Look at the example in std::remove_if
AFAICS, you could replace the remove_if/erase into the if and simplify it to:
for(auto i=blocks.begin(); i != blocks.end(); )
{
totalEnergy.push_back(abs(this->energy(*i)));
totalZeroCross.push_back(zerocross(*i));
if(!totalEnergy.rbegin() > totalSum || totalZeroCross.rbegin() < ZeroThres)
{
i = blocks.erase(i);
} else {
++i;
}
}
remove_if, and similar algorithms, expect a function, not a value, i.e. you should pass a function pointer or functor that an double const & and returns a bool
bool hasInsufficientEnergy(double const & element) {
// return true if it should be removed, false otherwise
}
and then
iniMatrix::iterator erase_after = remove_if(blocks[i].begin(), blocks[i].end(),
&hasInsufficientEnergy);
will work

map, lambda, remove_if

So, i've problem with std::map, lambda and stl algorithm(remove_if). Actually, same code with std::list or std::vector works well.
My test example :
#include <map>
#include <iostream>
#include <algorithm>
struct Foo
{
Foo() : _id(0) {}
Foo(int id) : _id(id)
{
}
int _id;
};
typedef std::map<int, Foo> FooMap;
int main()
{
FooMap m;
for (int i = 0; i < 10; ++i)
m[i + 100] = Foo(i);
int removeId = 6;
// <<< Error here >>>
std::remove_if(m.begin(), m.end(), [=](const FooMap::value_type & item) { return item.second._id == removeId ;} );
for (auto & item : m )
std::cout << item.first << " = " << item.second._id << "\n";
return 0;
}
Error message :
In file included from /usr/include/c++/4.6/utility:71:0,
from /usr/include/c++/4.6/algorithm:61,
from main.cxx:1:
/usr/include/c++/4.6/bits/stl_pair.h: In member function ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_T1, _T2>&&) [with _T1 = const int, _T2 = Foo, std::pair<_T1, _T2> = std::pair<const int, Foo>]’:
/usr/include/c++/4.6/bits/stl_algo.h:1149:13: instantiated from ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = std::_Rb_tree_iterator<std::pair<const int, Foo> >, _Predicate = main()::<lambda(const value_type&)>]’
main.cxx:33:114: instantiated from here
/usr/include/c++/4.6/bits/stl_pair.h:156:2: error: assignment of read-only member ‘std::pair<const int, Foo>::first’
I don't understand what's wrong here. So, i gladly to read some advices/directions about it. My goal - use new lambda-style with std::map and algorithms, such as remove_if.
g++ 4.6, -std=c++0x.
The problem is that std::map<K,V>::value_type is std::pair<const K, V>, aka .first is const and not assignable. Lambdas have nothing to do with the problem here.
std::remove_if "removes" items by moving the elements of the container around, so that everything that does not fit the predicate is at the front, before the returned iterator. Everything after that iterator is unspecified. It does that with simple assignment, and since you can't assign to a const variable, you get that error.†
The name remove can be a bit misleading and in this case, you really want erase_if, but alas, that doesn't exist. You'll have to make do with iterating over all items and erasing them by hand with map.erase(iterator):
for(auto it = map.begin(), ite = map.end(); it != ite;)
{
if(it->second._id == remove_id)
it = map.erase(it);
else
++it;
}
This is safe because you can erase individual nodes in the tree without the other iterators getting invalidated. Note that I did not increment the iterator in the for loop header itself, since that would skip an element in the case where you erase a node.
† By now, you should have noticed that this would wreak havoc in the std::map's ordering, which is the reason why the key is const - so you can't influence the ordering in any way after an item has been inserted.
You could use find and erase for the map. It's not as convenient as remove_if, but it might be the best you've got.
int removeId = 6;
auto foundIter = m.find(removeId);
// if removeId is not found you will get an error when you try to erase m.end()
if(foundIter != m.end())
{
m.erase(foundIter);
}

Errors when using map() in C++

I wrote a piece of code and used map and vector but it shows me something I can't get. I'll be thankful if someone help me in this way and correct my code or give me some hints.
The code is:
// For each node in N, calculate the reachability, i.e., the
// number of nodes in N2 which are not yet covered by at
// least one node in the MPR set, and which are reachable
// through this 1-hop neighbor
std::map<int, std::vector<const NeighborTuple *> > reachability;
std::set<int> rs;
for (NeighborSet::iterator it = N.begin(); it != N.end(); it++)
{
NeighborTuple const &nb_tuple = *it;
int r = 0;
for (TwoHopNeighborSet::iterator it2 = N2.begin (); it2 != N2.end (); it2++)
{
TwoHopNeighborTuple const &nb2hop_tuple = *it2;
if (nb_tuple.neighborMainAddr == nb2hop_tuple.neighborMainAddr)
r++;
}
rs.insert (r);
reachability[r].push_back (&nb_tuple);
}
/*******************************************************************************/
//for keepping exposition of a node
std::map<Vector, std::vector<const NeighborTuple *> > position;
std::set<Vector> pos;
for (NeighborSet::iterator it = N.begin(); it != N.end(); it++)
{
NeighborTuple nb_tuple = *it;
Vector exposition;
pos.insert (exposition);
position[exposition].push_back (&nb_tuple);
}
and the errors are for this line: position[exposition].push_back (&nb_tuple);
and the errors are:
/usr/include/c++/4.1.2/bits/stl_function.h: In member function ‘bool std::less<_
Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = ns3::Vector3D]’:
/usr/include/c++/4.1.2/bits/stl_map.h:347: instantiated from ‘_Tp& std::map<_K
ey, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = ns3::Vector3D, _Tp = std::vector<const ns3::olsr::NeighborTuple*, std::allocator<const ns3::olsr::NeighborTuple*> >, _Compare = std::less<ns3::Vector3D>, _Alloc = std::allocator<std::pair<const ns3::Vector3D, std::vector<const ns3::olsr::NeighborTuple*, std::allocator<const ns3::olsr::NeighborTuple*> > > >]’
../src/routing/olsr/olsr-routing-protocol.cc:853: instantiated from here
/usr/include/c++/4.1.2/bits/stl_function.h:227: error: no match for ‘operator<’ in ‘__x < __y’
debug/ns3/ipv6-address.h:432: note: candidates are: bool ns3::operator<(const ns3::Ipv6Address&, const ns3::Ipv6Address&)
debug/ns3/nstime.h:475: note: bool ns3::operator<(const ns3::Time&, const ns3::Time&)
debug/ns3/ipv4-address.h:305: note: bool ns3::operator<(const ns3::Ipv4Address&, const ns3::Ipv4Address&)
debug/ns3/address.h:231: note: bool ns3::operator<(const ns3::Address&, const ns3::Address&)
debug/ns3/type-id.h:376: note: bool ns3::operator<(ns3::TypeId, ns3::TypeId)
Thanks in advance.
Bahar
std::map is a sorted container of pairs. As such, keys in the map must have operator <() defined. Make sure Vector has the less-than operator defined.
For example:
class Vector {
int len, ang;
friend bool operator<(const Vector&, const Vector&);
};
bool operator<(const Vector& v1, const Vector& v2)
{
return true_if_v1_is_less_than_v2(); // you define what "less than" means
}
Of course, there other ways to do this. You may make operator< a member function. Or you may have the two member data public and the operator a non-member, non-friend function. Or you may define operator< in an anonymous namespace, to enhance information hiding. Or you may use a comparator other than operator<.
You declared the position object as followed : std::map<Vector, std::vector<const NeighborTuple *> > position;
And you are trying to push NeighborTuple * inside...
Try using const NeighborTuple *
I notice that you seem to have a pushback line that compiles and a line that does not.
The difference might be that you have a const in the first case
NeighborTuple const &nb_tuple = *it;