I have a vector of maps:
typedef map<string, string> aMap;
typedef vector<aMap> rVec;
rVec rows;
How can I remove some elements from rows?
The following code does not work.
struct remove_it
{
bool operator() (rVec& rows)
{
// Validation code here!
}
};
rVec::iterator it = remove(rows.begin(), rows.end(), remove_it());
rows.erase(it, rows.end());
I got the following error.
error: no matching function for call to 'remove(std::vector<s
td::map<std::basic_string<char>, std::basic_string<char> > >::iterator, std::vec
tor<std::map<std::basic_string<char>, std::basic_string<char> > >::iterator, mai
n(int, char**)::remove_it)'
Thanks.
1) First off: please provide a single compilable example.
Your code posted above is problematic as rVec and rowsVector have been interchanged (you would have seen that yourself if you had posted real code).
2) You are using the wrong remove. It should be remove_if
3) It is normal for the functor to be const
4) The operator() should get object of type aMap (as that is what is in your vector) not a reference back to the vector.
5) Don't be lazy add std:: in-front of objects in the standard namespace.
rather than using using namespace std;
#include <map>
#include <vector>
#include <string>
#include <algorithm>
typedef std::map<std::string, std::string> aMap;
typedef std::vector<aMap> rVec;
rVec rows;
struct remove_it
{
// Corrected type here
bool operator() (aMap const& row) const // const here
{
// Validation code here!
return true;
}
};
int main()
{
// _if herer
rVec::iterator it = std::remove_if(rows.begin(), rows.end(), remove_it());
rows.erase(it, rows.end());
}
remove expects a value. You are trying to use a functor, you need to use remove_if for that.
Also, your functor needs to accept an object of type aMap, not rVec.
remove_if is what you want, not remove.
Related
I have a std::vector of std::variant elements with type int or std::set<int>. I want to loop over this vector and insert an extra item if the iterated element is of type std::set<int>. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
#include <variant>
#include <set>
#include <vector>
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for (int i = 0; i < var_vec.size(); ++i)
{
// if the element var_vec[i] is of type std::set<int>
if (var_vec[i].index() == 1) var_vec[i].insert(888); // !ERROR! How to achieve this?
}
return 0;
}
Error message:
error: '__gnu_cxx::__alloc_traits<std::allocator<std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >, std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >::value_type' {aka 'class std::variant<int, std::set<int, std::less<int>, std::allocator<int> > >'} has no member named 'insert'
There's nothing wrong with calling index() at runtime, this happens all the time. The real problem is:
var_vec[i].insert(888);
var_vec[i] is a std::variant. It does not have a method called insert().
std::get<1>(var_vec[i]).insert(888);
This gives you the variant's set, which will happily allow you to insert() something.
One problem with this overall approach is that if you, for some reason, want to modify your variant, and that std::set is no longer its 2nd alternative, for some reason, all of the above logic will break again.
You should consider using std::holds_alternative, instead of index(), and using a type with std::get instead of an index, which will automatically adapt to this kind of a change.
I want to loop over this vector and insert an extra item if the iterated element is of type std::set. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
You don't need to check the type at runtime, you could just use std::visit() and check if the variant's type is std::set<int> at compile-time:
// Others headers go here...
#include <type_traits>
// ...
for (int i = 0; i < var_vec.size(); ++i)
// if the element var_vec[i] is of type std::set<int>
std::visit([](auto&& arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::set<int>>)
arg.insert(888);
}, var_vec[i]);
The filtering can be done via ranges::views::filter, and we can use std::visit to make up a predicate.
To do so we need to feed std::visit with a predicate on the types. A predicate on types can be made up as a set of overlaoded functions that each takes a type and all return a bool; you can create it via boost::hana::overload.
However, to filter the variants we need to feed std::visit only with its first argument, and to leave its second parameter free; one way to do it is to wrap std::visit in a nested lambda, like this:
auto visit_with = [](auto const& fs){
return [&fs](auto const& xs) {
return std::visit(fs, xs);
};
};
Another way is to use BOOST_HOF_LIFT and boost::hana::curry the result to make it partially applicabale, which is all done in one line:
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
Here's the complete code:
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/view/filter.hpp>
#include <set>
#include <variant>
#include <vector>
using namespace boost::hana;
using namespace ranges::views;
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
// predicate
auto constexpr true_for_sets = overload(
[](std::set<int> const&){ return true; },
[](auto const&){ return false; });
// function object wrapping std::visit
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
// filter
auto var_vec_out = var_vec | filter(visit_with(true_for_sets));
}
And this is all about the filtering you mention in the title. However, in the body of the question, you're not actually doing a filtering, but something else, as you are modifying the set as you traverse it.
As was already pointed out you're calling insert on an std::variant and not the std::set.
One approach I'd use is using std::get_if like so:
#include <variant>
#include <set>
#include <vector>
int main( ) {
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for ( auto& v : var_vec ) {
// If the variant does not hold a set, get_if returns a nullptr.
if ( auto set{ std::get_if<std::set<int>>( &v ) } ) {
set->insert( 888 );
}
}
// Or you could use holds_alternative with get, but this isn't as clean.
for ( auto& v : var_vec ) {
if ( std::holds_alternative<std::set<int>>( v ) ) {
auto set{ std::get<std::set<int>>( v ) };
set.insert( 999 );
}
}
}
This allows you to both test for the type and use the contained alternative.
The vector constructor calls in the statement below construct vectors with 50 elements of identical values in each vector as values in the sviMap. The statement repeats vector<int> three times. Is there a more concise way to achieve this overall initialization of the sviMap where vector<int> appears only once in the statement?
map<string, vector<int>> sviMap { {"Leo", vector<int>(50, 101)} , {"Brad", vector<int>(50, 201)} };
Strictly speaking, no. You need to have template specialization and two instances of the vector.
But if you need to have "this text once", this workaround could work for you:
using vi = vector<int>;
map<string, vi> sviMap{ {"Leo", vi(50, 101)} , {"Brad", vi(50, 201)} };
Formally, this is not "the statement", but two statements, but the vector appears only once. If your goal is to have one point for changes, this could work.
I wanted to correct my mistake, so here's another solution.
And this one will result in vectors of the correct length.
I just used inheritance to create a vector type with a constructor that has the desired behavior, so you can then use initializer lists without extra specifics
#include <cassert>
#include <map>
#include <vector>
#include <string>
template<typename type_t>
class my_vector :
public std::vector<type_t>
{
public:
my_vector() = default;
my_vector(const size_t size, const type_t value) :
std::vector<type_t>(size, value)
{
}
};
int main()
{
std::map<std::string, my_vector<int>> sviMap{ { "Leo", {50, 101}}, { "Brad", {50, 201}} };
auto vec = sviMap["Leo"];
auto value = vec[23];
}
You can do it like this, just use nested initializers
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> sviMap
{
{"Leo", {50, 101}},
{"Brad", {50, 201}}
};
auto vec = sviMap["Leo"];
}
My header file contains a structure as shown:
#include <iostream>
#include <vector>
#include <string>
struct Fields {
std::string f_name, l_name, id;
Fields (std::string fn,
std::string ln,
std::string i): f_name{fn},
l_name{ln}, id{i} {
}
};
and the main program contains a string vector "values" which actually contains strings extracted from a file
#include "my_header.h"
int main() {
std::vector <std::string> values = {"xyz", "p", "30"}; //have given values to it as example
static vector<Fields> field_values;
for (uint32_t vl=0; vl<values.size(); vl++){
field_values.emplace_back(values[vl]);
}
return 0;
}
But the above code gives an error:
error: no matching function for call to
‘Fields::Fields(std::basic_string&)’ { ::new((void *)__p)
_Up(std::forward<_Args>(__args)...); }
^
whereas when I give the individual values to emplace_back without using a for-loop, there is no error
field_values.emplace_back(values[0], values[1], values[2]); //gives no error
How can I emplace_back the values one at a time as in my code?
Fields's constructor takes 3 parameters. Your call to emplace_back provides one. emplace doesn't magically know you're going to provide more parameters later. It construct the object from the parameters you provide in that call.
You cannot do what you're trying to do: loop over elements of a container and pass those elements as a sequence of constructor parameters. Or at least, you can't do it with a for loop.
You can employ some metaprogramming and C++14 gymnastics, using std::index_sequence:
#include <utility>
template<typename T, typename U, size_t ...Ints>
void emplace_from_params(T &t, const U &u, std::index_sequence<Ints...>)
{
t.emplace_back(u[Ints]...);
}
int main()
{
std::vector <std::string> values = {"xyz", "p", "30"}; //have given values to it as example
vector<Fields> field_values;
emplace_from_params(field_values, values, std::make_index_sequence<3>{});
}
However, this is static. You cannot do this by dynamically passing values.size() to make_index_sequence.
This is my first time working with pairs, totally confused.
How to initialize a pair as to insert it in the map?
Should I include some standard library for this?
#include <string>
#include <map>
using namespace std;
class Roads
{
public:
map< pair<string,string>, int > Road_map;
void AddRoad( string s, string d )
{ int b = 2 ; Road_map.insert( pair<s,d>, b) ; } //pair<s,d> is wrong here.
};
You can use std::make_pair:
Road_map[make_pair(s, d)] = b;
Alternatively, you can construct an std::pair like so:
Road_map[pair<string,string>(s,d)] = b;
The std::make_pair approach saves you having to name the types of s and d.
Notice that the appropriate function here is operator[] and not insert. std::map::insert takes a single argument which is a std::pair containing the key and value you want to insert. You would have to do that like this:
Road_map.insert(pair<const pair<string,string>, int>(make_pair(s, d), b);
You can make this a bit prettier with typedef:
typedef map<pair<string,string>, int> map_type;
Road_map.insert(map_type::value_type(map_type::key_type(s, d), b));
Use std::make_pair instead. Like so:
#include <string>
using namespace std;
class Roads
{
public:
map< pair<string,string>, int > Road_map;
void AddRoad( string s, string d )
{
int b = 2 ;
Road_map[make_pair(s,d)] = b;
}
};
For a map<K, T>, the value_type is actually pair<K const, T>. However, the easiest way to access this is by using typedefs:
typedef std::pair<std::string, std::string> string_pair;
typedef std::map<string_pair, int> map_type;
// ...
Road_map.insert(map_type::value_type(map_type::key_type(s, d), b));
In C++11 you can use the easier emplace interface:
Road_map.emplace(map_type::key_type(s, d), b);
I would like to create and hold on to an iterator_range. The range is constructed based on a predicate (for this example, I look for even numbers).
I can do this, but it seems I must make a copy elements from the underlying vector that is being iterated.
Please look for the comments marked ">>>" in the sample below.
Is there a way to create the iterator_range and NOT have to create a duplicate of entries from the original vector?
I looked, and have not seen, an answer to this particular situation.
#include <vector>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/range/iterator_range.hpp>
using namespace std;
using namespace boost;
typedef boost::iterator_range<vector<int>::iterator> int_range;
template< class Range, class Pred >
boost::iterator_range< boost::filter_iterator< Pred, typename boost::range_iterator<Range>::type > >
make_filter_range( Range& rng, Pred pred ) {
return boost::make_iterator_range(
boost::make_filter_iterator(pred, boost::begin(rng), boost::end(rng)),
boost::make_filter_iterator(pred, boost::end(rng), boost::end(rng)) );
}
// This is the predicate evaluation function.
bool IsEvenFilter(int val) { return val % 2 == 0; }
void TestMakeIteratorRange()
{
std::vector<int> vals;
vals.push_back(1);
vals.push_back(4);
vals.push_back(7);
vals.push_back(11);
vals.push_back(16);
vals.push_back(19);
vals.push_back(28);
//>>> The following int_range line does not compile. Why?
//>>> How can I return an int_range?
//>>> int_range intRange = make_filter_range( vals, boost::bind(&IsEvenFilter, _1));
//>>> The following WILL work, but it forces a second copy of elements from vals.
std::vector<int> v2 = boost::copy_range< std::vector<int> >(
make_filter_range( vals, boost::bind(&IsEvenFilter, _1)));
int_range intRange = int_range(v2);
// Dump out contents
BOOST_FOREACH(int &val, intRange)
{
cout << " " << val;
}
cout << endl;
}
void main()
{
TestMakeIteratorRange();
}
int_range intRange = make_filter_range( vals, boost::bind(&IsEvenFilter, _1));
You have to store the type returned by make_filter_range. Which is not int_range.
This is incidentally why auto exists (in C++11); so that you don't have to type that return value if you want to store what the function returns. If you don't have access to C++11 auto, use BOOST_AUTO instead.
If you can't use that for some reason, you can also use any_range. Which, as the name suggests, can store any range for a specific type.
Also, consider using the proper Boost range-adapters, like boost::adaptors::filtered instead of make_filter_iterator.