std::transform needs special care with sets - c++

I don't understand why this snippet of code compiles:
#include <set>
#include <list>
#include <algorithm>
int modify(int i)
{
return 2*i;
}
int main (int args, char** argv)
{
std::set<int> a;
a.insert(1);
a.insert(2);
a.insert(3);
std::list<int> b; // change to set here
std::transform(a.begin(), a.end(), b.begin(), modify); // line 19
}
while, if I just change the type of b from std::list<int> to std::set<int> it fails at compilation time (at line 19) with the error: read-only variable is not assignable.
To use b as a set I need to change the transform line to
std::transform(a.begin(), a.end(), std::inserter(b, b.begin()), modify);
Why is that? I somehow guess the reason has to do with the fact that set is an associative container, while list is a sequence container, but I might be completely off the point here.
Edit
I forgot to mention: I tried this on gcc 3.4.2 and llvm 3.3 using the default standard (c++98). I tried again on llvm 3.3 using c++03 and I get the same behavior.

In your code without std::inserter, transform assigns to *b.begin(). In the case of set that's a const reference to an element (since C++11). Hence, a compile-time error.
In the case of the list it still assigns to *b.begin(), which compiles but has undefined behavior because the list has size 0. So b.begin() may not be dereferenced.
You are correct that this is to do with the fact that set is an associative container whereas list is a sequence. Associative containers don't let you modify the part of the element used as a key. In the case of set that part is the element, for map you can modify the value but not the key.
The whole point of std::inserter is to arrange that instead of assigning through an iterator, it calls insert.

First, the code as you have it exhibits undefined behavior, since the target list doesn't actually have space. Use a back_inserter to create space as you go.
As for the set, a set's elements are immutable. This is why you can't assign to a dereferenced iterator, even if you had space. But using the inserter is perfectly fine.

In C++03 this code compiles but results in undefined behavior - you cannot simply change values of set because they must be in ascending order. In c++11 that was fixed and set::iterator is a bidirectional iterator on const T, so you cannot change its values at all. std::inserter does not change existing values, instead it inserts new values in operator++ and so everything works

This record
std::transform(a.begin(), a.end(), b.begin(), modify);
is invalid even for std::list<int> (though it can be compiled for std::list or std::set in C++ 2003 where set has non-const iterator) because you defined an empty list.
std::list<int> b;
When such a record is used it is supposed that the output container already has elements in the range
[b.begin(), b.begin() + distance( a.begin(), a.end() ) )
because they are reassigned.
So if to consider a set then it is supposed that the set has already all required elements but you may not change them. When you use iterator adapter std::insert_iterator then it adds new elements in the container. So in this case you may use a set.

Related

Correct usage of C++ STL set_symmetric_difference?

In C++ I want to take two STL sets and produce a set which contains all the elements that are not in both sets, using set_symmetric_difference in algorithm.
I am getting a compile error "'_UDest': you cannot assign to a variable that is const", so I am misunderstanding how to use set_symmetric_difference.
Here is the code (C++ compiler is Visual Studio 2019):
set<unsigned short> a, b, diff;
// code to add entries to a and b
set_symmetric_difference(a.begin(), a.end(), b.begin(), b.end(), diff.begin()); // error C3892: '_UDest': you cannot assign to a variable that is const
You're passing an iterator into the empty set diff, as your "output iterator". Iterators don't work like this. diff.begin() refers to existing elements (of which there are none).
Set elements are immutable (they have to be, in order to maintain their ordering invariant), and that's why the particular error message you get relates to constness. However, without this factor, you'd still be trying to write into elements that don't exist.
You can instead use the special std::inserter magic iterator to wrap insertions into diff, like in the set_symmetric_difference usage example on cppreference (though that one uses sorted std::vector "sets" rather than std::set, so it gets to use std::back_inserter).
Here's an example:
std::set<unsigned short> a, b, diff;
// (code to add entries to a and b)
std::set_symmetric_difference(
a.begin(), a.end(),
b.begin(), b.end(),
std::inserter(diff, diff.begin()) // <---
);
You'll need #include <iterator> for this.

all_of function to check condition in all elements of part of an array

The following code checks if all elements in the declared array are odd numbers.
#include "stdafx.h"
#include <iostream> // std::cout
#include <algorithm> // std::all_of
#include <array> // std::array
int main () {
std::array<int,8> foo = {3,5,7,11,13,17,19,23};
if ( std::all_of(foo.begin(), foo.end(), [](int i){return i%2;}) )
std::cout << "All the elements are odd numbers.\n";
return 0;
}
(Sample taken from http://www.cplusplus.com/reference/algorithm/all_of)
I would like to check if all elements in the declared array starting from foo[2] are odd numbers.
Replacing foo.begin() with foo[2] does not work. I've tried many other things to make this work, all very basic (very basic C++ user here), without success. I do not want to resize the array for achieving this.
Ultimately what I'm looking for is having a loop where a condition is checked for on every element of part of an array, just as the for loop checks for a condition on any element of part of an array. This is relatively easy to achieve in R, and I'm hoping it may be equally easy to achieve in C++.
You can't use an iterator and an element here, they don't represent a range. In a more general sense, even attempting to use a pointer to an element and an iterator would not be well defined for all implementations of any given container either.
You should use std::next(it.begin(), 2) to increment to the begin() iterator to the foo[2] element and then you can iterate over the range with the two iterators.
std::all_of(std::next(foo.begin(), 2), foo.end(),
[](int i){/*...*/})
std::next() is more general and caters for iterators other than just random access iterators (e.g. for the alternate it.begin() + 2); but will still be performant for the type of iterator passed to it.

Checking for Item in std::vector<std::vector<std::string>> Using std::find

I have the following object
std::vector<std::vector<std::string>> vectorList;
Then I add to this using
std::vector<std::string> vec_tmp;
vec_tmp.push_back(strDRG);
vec_tmp.push_back(strLab);
if (std::find(vectorList.begin(), vectorList.end(), vec_tmp) == vectorList.end())
vectorList.push_back(vec_tmp);
The std::vector<std::string>s contained vectorList are only ever 2-dimensional and there are no duplicates. This works great, but I now only want to check if vectorList contains an item that index zero equal to the current strDrg. In C# I would not even be thinking about this, but this does not seem straight forward using C++. How can I find if a vector exists in vectorList where strDrg already exists in vectorList.at(i)[0]?
Note: I can use boost.
Use find_if with a lambda:
std::find_if(vectorList.begin(), vectorList.end(),
[&strDrg](const std::vector<std::string>& v) { return v[0] == strDrg; });
It seems you don't need the full power of vector for you inner elements. Consider using:
std::vector<std::array<std::string, 2>>
instead.
For doing exactly what you asked, std::find_if with a lambda as #chris proposed in comments is the best:
std::find_if(ob.begin(), ob.end(),
[&](const auto x){return x[0] == strDRG;});
// Replace auto with "decltype(ob[0])&" until
//you have a C++1y compiler. Might need some years.
But if you only ever have exactly two elements, consider using a std::array<...>, a std::pair<...> or a std::tuple<...> instead of the inner vector.
For tuple and pair, you need to access the first element differently:
pair : member first
tuple: use get<0>(x);

why doesn't std::remove_copy_if() actually remove?

Could this be the worst named function in the STL? (rhetorical question)
std::remove_copy_if() doesn't actually appear to do any removing. As best I can tell, it behaves more like copy_if_not.
The negation is a bit confusing, but can be worked around with std::not1(), however I might be misunderstanding something as I cannot fathom what this function has to do with removing - am I missing something?
If not, is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
Editing to add an example so readers are less confused.
The following program appears to leave the input range (V1) untouched:
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
using std::cout;
using std::endl;
int main (void)
{
std::vector<int> V1, V2;
V1.push_back(-2);
V1.push_back(0);
V1.push_back(-1);
V1.push_back(0);
V1.push_back(1);
V1.push_back(2);
std::copy(V1.begin(), V1.end(), std::ostream_iterator<int>(cout, " "));
cout << endl;
std::remove_copy_if(
V1.begin(),
V1.end(),
std::back_inserter(V2),
std::bind2nd(std::less<int>(), 0));
std::copy(V2.begin(), V2.end(), std::ostream_iterator<int>(cout, " "));
cout << endl;
std::copy(V1.begin(), V1.end(), std::ostream_iterator<int>(cout, " "));
cout << endl;
}
It outputs:
-2 0 -1 0 1 2
0 0 1 2
-2 0 -1 0 1 2
I was expecting so see something like:
-2 0 -1 0 1 2
0 0 1 2
0 0 1 2 ? ? ?
Where ? could be any value. But I was surprised to see that the input range was untouched, & that the return value is not able to be used with (in this case) std::vector::erase(). (The return value is an output iterator.)
Could this be the worst named function in the STL?
A bit of background information: in the standard library (or the original STL), there are three concepts, the containers, the iterators into those containers and algorithms that are applied to the iterators. Iterators serve as a cursor and accessor into the elements of a range but do not have a reference to the container (as mentioned before, there might not even be an underlying container).
This separation has the nice feature that you can apply algorithms to ranges of elements that do not belong to a container (consider iterator adaptors like std::istream_iterator or std::ostream_iterator) or that, belonging to a container do not consider all elements (std::sort( v.begin(), v.begin()+v.size()/2 ) to short the first half of the container).
The negative side is that, because the algorithm (and the iterator) don't really know of the container, they cannot really modify it, they can only modify the stored elements (which is what they can access). Mutating algorithms, like std::remove or std::remove_if work on this premise: they overwrite elements that don't match the condition effectively removing them from the container, but they do not modify the container, only the contained values, that is up to the caller in a second step of the erase-remove idiom:
v.erase( std::remove_if( v.begin(), v.end(), pred ),
v.end() );
Further more, for mutating algorithms (those that perform changes), like std::remove there is a non-mutating version named by adding copy to the name: std::remove_copy_if. None of the XXXcopyYYY algorithms are considered to change the input sequence (although they can if you use aliasing iterators).
While this is really no excuse for the naming of std::remove_copy_if, I hope that it helps understanding what an algorithm does given its name: remove_if will modify contents of the range and yield a range for which all elements that match the predicate have been removed (the returned range is that formed by the first argument to the algorithm to the returned iterator). std::remove_copy_if does the same, but rather than modifying the underlying sequence, it creates a copy of the sequence in which those elements matching the predicate have been removed. That is, all *copy* algorithms are equivalent to copy and then apply the original algorithm (note that the equivalence is logical, std::remove_copy_if only requires an OutputIterator, which means that it could not possibly copy and then walk the copied range applying std::remove_if.
The same line of reasoning can be applied to other mutating algorithms: reverse reverses the values (remember, iterators don't access the container) in the range, reverse_copy copies the elements in the range to separate range in the reverse order.
If not, is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
There is no such algorithm in the STL, but it could be easily implementable:
template <typename FIterator, typename OIterator, typename Pred>
FIterator splice_if( FIterator first, FIterator last, OIterator out, Pred p )
{
FIterator result = first;
for ( ; first != last; ++first ) {
if ( p( *first ) ) {
*result++ = *first;
} else {
*out++ = *first;
}
}
return result;
}
is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
The closest thing I can think of is std::stable_partition:
std::vector<int> v;
// ...
auto it = std::stable_partition(v.begin(), v.end(), pick_the_good_elements);
std::vector<int> w(std::make_move_iter(it), std::make_move_iter(v.end()));
v.erase(it, v.end());
Now v will contain the "good" elements, and w will contain the "bad" elements.
If not, is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
Not really. The idea is that the modifying algorithms are allowed to "move" (not in the C++ sense of the word) elements in a container around but cannot change the length of the container. So the remove algorithms could be called prepare_for_removal.
By the way, C++11 provides std::copy_if, which allows you to copy selected elements from one container to another without playing funny logic games with remove_copy_if.
You are right, that is what it does... std::remove_copy_if copies the vector, removing anything that matches the pred.
std::remove_if ... removes on condition (or rather, shuffles things around).
I agree that remove is not the best name for this family of functions.
But as Luc said, there's a reason for it working the way it does, and the GoTW item that he mentions explains how it works. remove_if works exactly the same way as remove - which is what you would expect.
You might also want to read this Wikibooks article.

Remove elements from a c++ vector where the removal condition is dependent on other elements

The standard way to remove certain elements from a vector in C++ is the remove/erase idiom. However, the predicate passed to remove_if only takes the vector element under consideration as an argument. Is there a good STL way to do this if the predicate is conditional on other elements of the array?
To give a concrete example, consider removing all duplicates of a number immediately following it. Here the condition for removing the n-th element is conditional on the (n-1)-th element.
Before: 11234555111333
After: 1234513
There's a standard algorithm for this. std::unique will remove the elements that are duplicates of those preceding them (actually, just like remove_if it reorganizes the container so that the elements to be removed are gathered at the end of it).
Example on a std::string for simplicity:
#include <string>
#include <iostream>
#include <algorithm>
int main()
{
std::string str = "11234555111333";
str.erase(std::unique(str.begin(), str.end()), str.end());
std::cout << str; // 1234513
}
Others mentioned std::unique already, for your specific example. Boost.Range has the adjacent_filtered adaptor, which passes both the current and the next element in the range to your predicate and is, thanks to the predicate, applicable to a larger range of problems. Boost.Range however also has the uniqued adaptor.
Another possibility would be to simply keep a reference to the range, which is easy to do with a lambda in C++11:
std::vector<T> v;
v.erase(std::remove_if(v.begin(), v.end(),
[&](T const& x){
// use v, with std::find for example
}), v.end());
In my opinion, there will be easier to use simple traversal algorithm(via for) rather then use std::bind. Of course, with std::bind you can use other functions and predicates(which depends on previous elements). But in your example, you can do it via simple std::unique.