Std::Back_Inserter for a Std::Set

std::back_inserter for a std::set?

set doesn't have push_back because the position of an element is determined by the comparator of the set. Use std::inserter and pass it .begin():

std::set<int> s1, s2;
s1 = getAnExcitingSet();
transform(s1.begin(), s1.end(),
std::inserter(s2, s2.begin()), ExcitingUnaryFunctor());

The insert iterator will then call s2.insert(s2.begin(), x) where x is the value passed to the iterator when written to it. The set uses the iterator as a hint where to insert. You could as-well use s2.end().

std::inserter with set - insert to begin() or end()?

You could use a custom functor instead of std::inserter and re-call out.end() every time a new element is inserted.

Alternatively, if your values are sorted descendingly, out.begin() will be fine.

Converting std::vector container into an std::set using std::transform

You probably can't create a set of char * unless all instances of extensionName with the same value point to the same char array (it would store unique pointers instead of unique values). If you use std::set<std::string> instead this will both work and only store unique values and solve your variable lifetime problem as std::string takes care of copying (or moving) itself for you where necessary:

auto lambdaFn = 
[](const SomeStruct& x) { return std::string(x.extensionName); };
std::set<std::string> xs;
std::transform(availableExtensions.begin(),
availableExtensions.end(),
std::inserter(xs, xs.begin()),
lambdaFn);

What happens if I use vector::begin() instead of std::back_inserter(vector) for output of set_intersection?

The important requirement for an output iterator is that it be valid and write-able for the range
[out, out+size of output).

Passing c.begin() will lead to the values being overwritten which only works if the container c holds enough elements to overwrite. Imagine that c.begin() returns a pointer to an array of size 0 - then you'll see the problem when writing *out++ = 7;.

back_inserter adds every assigned value to a vector (via push_back) and provides a concise way of making the STL-algorithms extend a range - it overloads the operators that are used for iterators appropriately.

Thus

 std::set_intersection(a.begin(),a.end(),b.begin(),b.end(),
c.begin());

invokes undefined behavior once set_intersection writes something to its output iterator, that is, when the set intersection of a and b isn't empty.

Can a conforming implementation silently extend the vector in this case, so that begin() is in fact an appending OutputIterator like back_inserter is?

Of course. It's undefined behavior. (This is a humorous approach of telling you that you shouldn't even consider using this, no matter the effects on any implementation.)

std::set_intersection and iterators

Yes, You can use std::inserter, instead of the std::back_inserter like this.

#include <iterator> // std::inserter

std::set<int> s3;
std::set_intersection(s.begin(), s.end(), s2.begin(), s2.end(),
std::inserter(s3, s3.end()));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Is this correct way to combine std::generate_n and std::back_inserter?

generate_n requires that its first argument satisfy OutputIterator, which back_insert_iterator does (its iterator_category is output_iterator_tag).

Potential issues with your code:

std::generate_n(std::back_inserter(mrbig),nitems,[](){return 'a'+(rand()%26);});
  • Calling mrbig.reserve(nitems) would be more efficient
  • You should use c++03 std::rand or c++11 uniform_int_distribution<> from <random> instead of c rand.
  • The parentheses () between the lambda capture and lambda body are unnecessary for a lambda taking no arguments, although some people prefer them
  • It is not guaranteed that in implementation character set the letters a-z form a contiguous block; for example, in EBCDIC, i and j are not adjacent. I believe the only portable form is to store a string "abcd...xyz" in your program and index into it: How can I write a single for loop running from a to z and A to Z in C?

Why cant I use std::copy with std::string to another std::string?

The problem is r is an empty std::string, it contains no chars. std::copy is trying to copy-assign chars since r.begin(), which leads to UB.

You can make r containing 5 elements in advance.

std::string r(5, '\0');

Or

std::string r;
r.resize(5);

Or use std::back_inserter.

std::string r;
std::copy(s.rbegin(),s.rend(), std::back_inserter(r));

Is it safe to use parallel execution policies with std::back_inserter?

Calling push_back on a std::vector does not match the requirements of par_unseq or par.

So no, it isn't safe.

By saying par you are promising that access to the iterators used for input and output contains no race conditions. That fails here.

par_unseq requires more promises from you; basically that you are both data-race free, and your algorithm is suitable for vectorization (doing things in batches).

push_back does not qualify, and back_inserter is defined in terms of push_back.

On the other hand:

std::vector<std::string> src(100, "test"), dst(100, "");
std::move(std::execution::par_unseq, src.begin(), src.end(), dst.begin());

this is valid, because you are allowed to parallel and concurrently access individual elements of a vector (except vector<bool>)

Moving a vector into an unordered_set

This solution actually requires more characters, but it does express the intent more directly:

std::unordered_set<T> ht(std::make_move_iterator(v.begin()),
std::make_move_iterator(v.end()));

Why I could use push_back in map but not the back_inserter in set?

my_map[ln].push_back(cn) does not call push_back on the map (my_map), it calls push_back on the maps mapped_type which is vector<string> - you access this with operator[] (my_map[ln]).

Your statement my_map[ln].push_back(cn) is essentially equivalent to:

vector<string>& v = my_map[ln];
v.push_back(cn);


Related Topics



Leave a reply



Submit