+= operator on a pair and make_pair with templates - c++

I came across the following while reading codebase of a library.
errorMap, used in the below code is defined as:
map <const string, pair<int, double>> errorMap;
And the relevant part of the code being:
errorMap["substitutions"] += make_pair<int,double>(targetLength, substitutions);
errorMap["insertions"] += make_pair<int,double>(targetLength, insertions);
errorMap["deletions"] += make_pair<int,double>(targetLength, deletions);
The above part is throwing this compilation error. When running the library through it's own build system, the code seems to be compiling. Can someone shed some light on what exactly is happening here?
PS: I already looked at the pair documentation at cppreference and other sites, none of them specify a += operator for a pair. This is the first time I'm encountering a make_pair with templated arguments, on which I can't find more information either.

It's not about the pair, it's about the map. The operator [] is used to insert or update elements in a map.
For example in a std::map<char, int> myMap{{'a', 27}, {'b', 3}, {'c', 1}};, I could do the following (as demonstrated in the page linked above):
myMap['a'] = 6; //to change the value associated to 'a'
myMap['d'] = 8; //to insert a new value
I could also do the following:
myMap['b'] += 9; //Now the value associated to b is 3 + 9 = 12
In the 3 lines of code posted in the question, the values associated with the strings inside of the brackets are being updated.
The operator+= have probably been overloaded for pairs with template. (Look at the answers to this question) This might be why, you're getting those errors instead of the following (replace char with string):
error: no match for ‘operator+=’ (operand types are ‘std::map<char, std::pair<int, double> >::mapped_type {aka std::pair<int, double>}’ and ‘std::pair<int, double>’)
Since the same operation doesn't reproduce the same errors, the poblem is from deeper implementations, for which you provided no context. They might be related to right and left values:
cannot convert ‘targetLength’ (type ‘int’) to type ‘int&&’
You [might][1] want to look at those answers for that.
[1]: Might because I'm not sure of what I'm saying. I wanted to contribute with a comment, but not enough rep, so I tried my best with an answer.

Related

Providing an allocator for Boost's `cpp_dec_float_100`

I have a dataset stored in .root file format (from the CERN ROOT framework) as type cpp_dec_float_100 (from the boost::multiprecision library). This data is read into an std::vector<cpp_dec_float_100>. By default, cpp_dec_float_100 is unallocated. If I were to try to read this data into a vector as-is, an std::bad_alloc is thrown. So, I've taken the advice of the Boost docs and provided a generic allocator, which seems to solve the issue (and appears to cut the size the resulting vector in half).
Ultimately, I want to pass this vector as an argument to a function that I've written, which performs binary search on a vector to find the element of that vector closest to a given value:
#include <boost/multiprecision/cpp_dec_float.hpp>
using Mult_t = boost::multiprecision::cpp_dec_float<100, int, allocator<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<100>>>>;
std::vector<Mult_t>::iterator search(std::vector<Mult_t> &vec, Mult_t value){
auto it = lower_bound(vec.begin(), vec.end(), value);
if(it != vec.begin()){
if(abs(value - *(it - 1)) < abs(value - *it)){
--it;
}
}
return it;
}
I'm using the "alias" Mult_t as the alternative is a bit of a mouthful.
So, given the vector vec and the value val, this finds the element in vec nearest to val.
If I use the cpp_dec_float_100 type as-is (i.e. Mult_t = boost::multiprecision::cpp_dec_float_100), this works great. However, when I attempt to provide an allocator, I'm given the error:
In module 'std' imported from input_line_1:1:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/bits/stl_algobase.h:965:18: error: invalid operands to binary expression ('boost::multiprecision::backends::cpp_dec_float<100, int,
std::allocator<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100,
int, void>, boost::multiprecision::expression_template_option::et_on> > >' and 'const
boost::multiprecision::backends::cpp_dec_float<100, int,
std::allocator<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100,
int, void>, boost::multiprecision::expression_template_option::et_on> > >')
if (*__middle < __val)
I don't quite understand what's going on here (I doubt seriously it has anything to do with the allocator), and the error message isn't terribly insightful.
Your problem has nothing to do with allocator, just because cpp_dec_float<...> has no operator<(), only number<cpp_dec_float<...>> supports.
You should redefine your Mult_t as:
using namespace boost::multiprecision;
using Mult_t = number<
cpp_dec_float<100, int, std::allocator<number<cpp_dec_float<100>>>>>;

understanding how zip in range-v3 works

I am trying to understand how ranges::views::zip works in range-v3. I understand that it is a range that allows to iterate on several ranges in one single loop by creating a tuple of the elements in different ranges.
std::vector<int> v1 = {0, 1, 2};
std::vector<char> v2 = {'a', 'b', 'c'};
auto zip = ranges::views::zip(v1,v2);
// zip(v1,v2) = [(0,a), (1,b), (2,c)]
ranges::actions::sort(zip);
std::sort(std::begin(zip), std::end(zip));
The sort using ranges::actions works fine but std::sort doesnt compile and gives the following error
/usr/include/c++/9.3.0/bits/stl_algobase.h:151: error: no matching function for call to ‘swap(concepts::return_t<ranges::common_pair<int&, double&>, void>, concepts::return_t<ranges::common_pair<int&, double&>, void>)’
151 | swap(*__a, *__b);
| ~~~~^~~~~~~~~~~~
Why is this happening?
I have also tried to remove elements in both containers at the same time. ranges::actions::unique doesn't compile with the following error:
/home/jjcasmar/projects/cpfsofaplugin/src/CPFSofaPlugin/minimalExample.cpp:27: error: no match for call to ‘(const ranges::actions::action_closure<ranges::actions::unique_fn>) (ranges::zip_view<ranges::ref_view<std::vector<int, std::allocator<int> > >, ranges::ref_view<std::vector<double, std::allocator<double> > > >&)’
27 | ranges::actions::unique(v1Andv2);
| ^
but auto lastIt = std::unique(std::begin(v1Andv2), std::end(v1Andv2)) compiles find, although I dont know how to get the internal iterators of the zip to be able to erase past the end elements.
I dont really understand how this works under the hood and why in some cases std algorithms work fine but in some cases it doesn't. Can someone give some explanation about this?
Look at the types:
auto zip = ranges::views::zip(v1, v2);
// ranges::zip_view<
// ranges::ref_view<std::vector<int>>
// ranges::ref_view<std::vector<char>>
// >
auto begin = std::begin(zip);
// ranges::basic_iterator<
// ranges::iter_zip_with_view<
// ranges::detail::indirect_zip_fn_,
// ranges::ref_view<std::vector<int>>,
// ranges::ref_view<std::vector<char>>
// >::cursor<false>
// >
using traits = std::iterator_traits<decltype(begin)>;
static_assert(std::is_same_v<traits::value_type, std::pair<int, char>>);
static_assert(std::is_same_v<traits::reference, ranges::common_pair<int&, char&>>);
The value_type type is a std::pair of values. The reference type is a ranges::common_pair of references.
std::sort uses std::iter_swap which is specified in terms of dereferencing the iterators and calling std::swap. So std::sort will try to swap two ranges::common_pair of references. On the other hand, ranges::actions::sort uses ranges::iter_swap which is customized to handle pairs and tuples of references.
Pairs and tuples of references are/were second class citizens in the standard library.
ranges::actions::unique requires an erasable range which evidently this does not satisfy.
Added
The documentation for range-v3 is sparse. To find information such as the above, there is of course looking at the source for range-v3, quick experimentation on godbolt.org (range-v3 is an available library), and "standard" C++ tricks to find the type of a variable (e.g., calling a function template which is declared but not defined, with the variable's type as the template argument, and seeing which instantiation is called).
To comment more on unique, ranges::action::unique does not return an iterator. It erases the non-unique elements and returns a range (see the source). In part of the compiler error which was omitted, the error references the fact that the range is not erasable (buried in a huge error).
ranges::unique returns an iterator and can be called without error. This is a basic_iterator<...>. One option is to use ranges::distance to find the distance from the begin zip iterator and use this to get the underlying iterator:
auto zip_uniq_iter = ranges::unique(zip);
auto first_uniq_iter = std::next(v1.begin(), ranges::distance(ranges::begin(zip), zip_uniq_iter));
You cannot use std::sort on views.
But you can transform your view to a vector and then it works:
https://godbolt.org/z/_FvCdD
I can recommend the following sites for more information on ranges:
https://www.walletfox.com/course/quickref_range_v3.php
https://mariusbancila.ro/blog/2019/01/20/cpp-code-samples-before-and-after-ranges/

How do you use a range-based for loop on the values of a std::map?

I'm trying to use std::map::operator[] to iterate over the values of a std::map with a range-based for loop, but the following doesn't compile:
#include <iostream> // cout, endl
#include <map> // map
#include <set> // set
using namespace std;
int main () {
using namespace std;
const map<int, set<int>> m {{2, {201, 202}}, {3, {301, 302}}};
for (int v : m[2])
cout << v << endl;
return 0;
}
Here's the compiler's error message:
Test.c++:18:19: error: no viable overloaded operator[] for type 'const map<int, set<int> >'
for (int v : m[2])
The followup question is, Given that there are two versions of at(), why aren't there two versions of []?
map::operator[] inserts a new element into the map if the key isn't found, hence it cannot be const, and cannot be called on a const map.
Use m.at(2) instead.
operator[] will do one of two things. It will find the element at that location if it exists and return it. If there is no element at that location, it will value-initialize it, and then return it.
As such, it is neither logically nor programmatically const.
While you may say "but there is an element 2", the constness of an operation depends only on the types of the arguments, not the value of the arguments. And m[int] isn't guaranteed to have a valid element there.
To fix this, you can replace m[2] with m.find(2)->second. This does undefined behavior if 2 is missing from the map, but if 2 is present it will evaluate to the same thing as m[2]. Alterantively, m.at(2) will again do the same thing if 2 is present as a key, and throw an exception if is not present.
As an aside, when working on such a problem, try breaking your code down into smaller pieces. m[2]; all by itself on a line will fail to compile. In fact, the error message told you that it could not find an operator[] -- that might have given you a clue what was wrong.
Your map m is declared const meaning that you can only call const functions on it.
std:map::operator[] is not a such function, since it will modify the map if the specified key is not found.
What you need is std::map::at, which is const so you can call it on your const map.

c++ stl iterators

I am getting a lot of errors trying to use an iterator for three dimensional vector of sets of ints. See the following code (which is just select pieces, because the whole thing is too long; I think this should be enough to see what is wrong, but let me know if it isn't):
vector<vector<vector<set<int> > > > particles_celllist;
vector<vector<vector<set<int> > > >::iterator cell_iter;
map<int,map<int,Particle> > particle_grid;
for (cell_iter=particles_celllist[wx][wy][wz].begin();cell_iter!=particles_celllist[wx][wy][wz].end();cell_iter++)
{
double distance_to_cell=sqrt(pow(particles[*cell_iter].position().y()-(wy)*ygridlength,2)+
pow(particles[*cell_iter].position().z()-(wz)*zgridlength,2));
if (distance_to_cell<input_data.diam_large())
{
particle_grid[box_counter][*cell_iter]=particles[*cell_iter];
}
}
Note: wx, wy, wz, and box_counter are ints, ygridlength and zgridlength are doubles, and Particle::position::y (or ::z) and input_data::diam_large return doubles.
I get a multitude of errors:
no match for operator "=" in
"cell_iter=particles_celllist[wx][wy][wz].begin()"
no match for operator "!=" in
"cell_iter!=particles_celllist[wx][wy][wz].end()"
no match for operator "[]" whenever I used [*cell_iter] to call
something
I get the feeling like the error somehow stems from the iterator itself, but I haven't been able to figure it out.
You want set<int>::iterator cell_iter;. Just look again carefully at whose begin() function you're calling.
In C++11 you would of course just have said
auto cell_iter = particles_celllist[wx][wy][wz].begin()
and never noticed that this is hard :-)
particles_celllist[wx][wy][wz] is of type set<int>, so particles_celllist[wx][wy][wz].begin() is of type set<int>::iterator

Iterator of Vector of Vectors fails to work with a 1-D Vector

In my program, I have a vector of vector of ints. Now I want to take one vector from the vector of vectors and have it manipulated in another vector container, but I get the error...
|error: conversion from '__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >' to non-scalar type 'std::vector<int, std::allocator<int> >' requested|
An example of what I am trying to do is as follows....
#include <vector>
using namespace std;
vector<vector<int> > k (13,5);
void some_funct() {
vector<int> new_v (k[2].begin(), k[2].end()); //This line is what throws the error
//here I do some stuff with new_v (e.g. sort it)
}
I'm not sure what I am doing wrong. I tried a couple of things like assigning the begin() and end() iterators to const iterator types... vector<int>::const_iterator it = k[2].begin(); but that didn't work either.
This should work (because k[x] would be a vector) but I don't know what is going wrong. Any help is appreciated!
EDIT:
After revision of my code, I noticed that there actually was an error. Instead of doing vector<int> new_v (k[2].begin(),k[2].end()); I did vector<int> new_v = (k[2].begin(),k[2].end());.
I would like to thank Rob for giving me the initiative to copy and paste my code into SO, where I noticed my mistake.
Thank you for your help!
It is hard to know, because you haven't posted your actual code into the question. I suspect that you mis-copied the code from your project into Stack Overflow.
The offending line in your project looks something like this:
vector<int> new_v = (k[2].begin(), k[2].end());
Note the extra =.
You are initializing new_v with an expression of type vector::iterator, which won't work. The statement you typed into SO, however, will work:
vector<int> new_v (k[2].begin(), k[2].end());
As will this:
vector<int> new_v = vector(k[2].begin(), k[2].end());
Or either of these:
vector<int> new_v(k[2]);
vector<int> new_v = k[2];
See https://ideone.com/uK8Xg and the corresponding error message.
The error message tells us that you're trying to (re-)create a vector from a vector::iterator. since vector does not support this kind of constructor or copy assignment, the compiler would raise an error. However your posted code here is valid.