How to mix auto with brace-initialization to return a std::pair properly? - c++11

I have this example which is a function that returns a std::pair which holds a string value and its size.
std::pair<std::string, int> getLastPair(const vector<string>& vec) {
return{ vec.back(), vec.back().size() };
}
int main(){
vector<string> names{ "An", "Apple", "A", "Day", "Keeps", "The",
"Doctor", "Away"};
auto p{getLastPair(names)}; // ok p is a pair<string, int>
std::cout << p.first << " " << p.second << std::endl;
std::cout << typeid(p).name() << std::endl;
auto p2 = { getLastPair(names) }; // why it is not a pair but an initializer list?
std::cout << typeid(p2).name() << std::endl; // initializer list
std::pair<std::string, int> p3 = { getLastPair(names) }; // ok a pair
std::cout << p3.first << " : " << p3.second << std::endl;
decltype (getLastPair(names)) p4 = { getLastPair(names) }; // ok
std::cout << p4.first << " : " << p4.second << std::endl; // ok
}
Why the first initialization returns a pair as expected but the second one p2 is not a pair but an initializer_list instead?
As you can see p3 works fine (returns a pair) aslong as I've provided the type explicitly!?
Is the problem in Type Specifier auto?
Also p4 works fine with decltype.

Related

How to append more items to an existing vector contained in the value field of a std::map?

I have a std::vector<std::string>>. Following is my full program:
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main() {
std::cout << " -- Beginining of program -- " << std::endl;
std::map<std::string, std::vector<std::string>> my_map_2;
std::vector<std::string> s = {"a", "b", "c"};
my_map_2.insert(std::make_pair("key1", s));
std::vector<std::string> s2 = {"d", "e", "f"};
my_map_2.insert(std::make_pair("key1", s2));
for(auto const &map_item: my_map_2) {
std::cout << map_item.first << " " << map_item.second[0] << std::endl;
std::cout << map_item.first << " " << map_item.second[1] << std::endl;
std::cout << map_item.first << " " << map_item.second[2] << std::endl;
std::cout << map_item.first << " " << map_item.second[3] << std::endl;
std::cout << map_item.first << " " << map_item.second[4] << std::endl;
std::cout << map_item.first << " " << map_item.second[5] << std::endl;
}
std::cout << " -- End of program -- " << std::endl;
return 0;
}
Problem:
I don't see the items of s2 when I print values of my_map_2. I see them only if I add s2 with a new key! If I do my_map_2.insert(std::make_pair("key2", s2)) instead of my_map_2.insert(std::make_pair("key1", s2)), I do see the items.
Question:
So, my question is, how to I append more items to the vector pointed to by key1 of my_map_2?
The below fails because the key is already taken:
std::vector<std::string> s2 = {"d", "e", "f"};
my_map_2.insert(std::make_pair("key1", s2)); // fails
To append to the mapped vector, you could do like this:
auto& vec = my_map_2["key1"]; // get reference to the existing vector
vec.insert(vec.end(), s2.begin(), s2.end()); // append to it
To view the keys and all the values in the vector you could change your loop to this:
for(auto const&[key, value]: my_map_2) {
for(const std::string& str : value) {
std::cout << key << ' ' << str << '\n';
}
}
my_map_2["key1"] is always a valid vector. You can insert into it directly
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main() {
std::cout << " -- Beginining of program -- " << std::endl;
std::map<std::string, std::vector<std::string>> my_map_2;
std::vector<std::string> s = {"a", "b", "c"};
my_map_2["key1"].insert(my_map_2["key1"].end(), s.begin(), s.end());
std::vector<std::string> s2 = {"d", "e", "f"};
my_map_2["key1"].insert(my_map_2["key1"].end(), s2.begin(), s2.end());
for(auto const &map_item: my_map_2) {
for(auto const &value: map_item.second) {
std::cout << map_item.first << " " << value << std::endl;
}
}
std::cout << " -- End of program -- " << std::endl;
return 0;
}
Get iterator to key1, and just pushs back new items to existing vector:
std::vector<std::string> s2 = {"d", "e", "f"};
auto it = my_map_2.find("key1");
if (it != my_map_2.end())
std::move(s2.begin(), s2.end(), std::back_inserter(it->second));
else
my_map_2.insert(std::make_pair("key1",std::move(s2)));
To see: d,e,f you have to access 3,4 and 5 indices of vector. (You want to append new items, or just override existed items for given key?)

c++11 how to check template parameter pack has N args before calling function with N args

Following on from this extracting a template parameter pack with different types into a vector of doubles produces warnings and cigien's answer.
I have the following code:
enum class p_type {p1, p2, p3};
class testerx
{
public:
void process1(double a)
{
std::cout << "1" << a << std::endl;
};
void process2(double a, double b)
{
std::cout << "2" << a << " " << b << std::endl;
};
void process3(double a, double b, double c)
{
std::cout << "3" << a << " " << b << " " << c << std::endl;
};
};
// The template type
template<typename TESTER, typename... ARGS>
class tester_templatex
{
public:
explicit tester_templatex(p_type type) : m_type(type) {};
void process(ARGS... args)
{
// Create a vector to put the args into. use double since that can hold all of the types
// that I am using
size_t param_count = sizeof...(args);
std::cout << "PARAM COUNT X " << param_count << std::endl;
std::vector<double> args_vect = {static_cast<double>(args)...};
for (auto arg : args_vect)
{
std::cout << "arg: " << arg << std::endl;
}
// Now call the tester
std::cout << "running tester: ";
switch (m_type)
{
case p_type::p1:
if constexpr (sizeof...(args) == 1)
m_tester.process1(args...);
break;
case p_type::p2:
if constexpr (sizeof...(args) == 2)
m_tester.process2(args...);
break;
case p_type::p3:
if constexpr (sizeof...(args) == 3)
m_tester.process3(args...);
break;
}
std::cout << std::endl;
};
p_type m_type;
TESTER m_tester;
};
main:
int main() {
tester_templatex<testerx, int> templatex1(p_type::p1);
tester_templatex<testerx, int, double> templatex2(p_type::p2);
tester_templatex<testerx, int, double, int> templatex3(p_type::p3);
templatex1.process(4);
templatex2.process(4, 5.123);
templatex3.process(4, 5.123, 6);
return 0;
}
Here I have test class with 3 different functions. I have a template class which picks the function to call based on the p_type (bad name - dont ask!).
This works for c++17 compiled code. But I only have c++11 where I need to run this code. c++11 does not support if constexpr:
case p_type::p3:
if constexpr (sizeof...(args) == 3)
m_tester.process3(args...);
break;
Without the if constexpr I get errors that the m_tester.process1/2/3 functions that don't match the parameter pack because they don't have the right number of parameters.
How can I fix this for c++11? - is it possible with a similar method?
Is there another way to extract N arguments from a parameter pack in c++11? - or some sort of type traits check?
For each of your functions have an overload that does nothing:
template<typename... ARGS>
void process3(ARGS&...) { }
and then just call the function without testing for the size of the pack:
case p_type::p3:
m_tester.process3(args...);
break;
This should pick the non-templated function when there are suitably many arguments, and the function template in other cases.

Why I cannot use a decltype in range-for with multi-dimension arrays?

I have a problem here. I am trying to use decltype in range-for loop for using multi-dimension array:
int a[][4]{
{0, 1, 2, 3 },
{4, 5, 6, 7 },
{8, 9, 10, 11}
};
for (auto& row : a) { // reference is needed here to prevent array decay to pointer
cout << "{";
for (auto col : row)
cout << col << ", ";
cout << "}" << endl;
}
decltype (*a) row{ *a};
cout << sizeof(row) << endl;
cout << typeid(row).name() << endl;
// for (decltype(*a) row : *a) {
// for (int col : row)
// cout << col << ", ";
// cout << endl;
// }
With auto I can easily iterate over the the array But with decltype it doesn't work for me.
What I get above if I uncomment the code is: cannot convert from int to int(&)[4].
That is because the line for(decltype(*a) row : *a) is incorrect. Try to read it correctly: for each array of 4 int from a, not from *a.
The code may look like:
for (decltype(*a) row : a) {
for (int col : row)
cout << col << ", ";
cout << endl;
}
Dereferencing a (*a) with decltype will yield an array of 4 integers. So the type is int[4]. Unlike using keyword auto where it yields int*.

problem with polygon_90_data and vertices

I create a boost polygon with 5 vertices, but when I display the vertices or
#include <boost/polygon/polygon.hpp>
.
.
.
void displayPolygon(boost::polygon::polygon_90_data<int> const& polygon, std::string name)
{
std::cout << std::endl << "polygon " << name << std::endl;
for (boost::polygon::polygon_90_data<int>::iterator_type it = polygon.begin(); it != polygon.end(); it++)
{
std::cout << (*it).x() << "/" << (*it).y() << "->";
}
std::cout << std::endl;
}
.
.
.
void testPolygon2()
{
//typedef
typedef boost::polygon::polygon_90_data<int> Polygon90Type;
typedef std::vector<Polygon90Type> Polygon90Set;
typedef boost::polygon::polygon_data<int> PolygonType;
typedef std::vector<PolygonType> PolygonSet;
typedef boost::polygon::point_data<int> PointType;
typedef std::vector<PointType> PointSet;
typedef boost::polygon::polygon_traits<Polygon90Type>::point_type Point;
Polygon90Type polygon_1;
Point pts_1[6] = { Point(0,0), Point(0,10), Point(5,12), Point(10,10), Point(10,0), Point(0,0)};
polygon_1.set(pts_1, pts_1 + 6);
std::cout << "polygon_1 area is " << boost::polygon::area(polygon_1) << std::endl;
std::cout << std::endl << __LINE__ << " :
Polygon90Set polygonSet;
polygonSet.push_back(polygon_1);
//an attempt to see result of bloating with 0
boost::polygon::bloat(polygonSet, 0, 0, 0, 0);
std::cout << "nb polygon bloated " << polygonSet.size() << std::endl;
for (auto polygon : polygonSet)
{
std::cout << std::endl << __LINE__ << "####################################" << std::endl;
displayPolygon(polygon, "2 - after being bloated");
std::cout << "area is " << boost::polygon::area(polygon) << std::endl;
}
}
the area it does not correspond to the polygon created.
the area should be 110 but is displayed as 100?
the vertices should be (0,0), (0,10), (5,12), (10,10), (10,0) but are (0/10), (5/10) ,(5/10), (10/10), (10/0), (0/0). It looks like the point (/5/12) is dismissed and replaced with point (5/10).
What am I doing wrong?
thanks.

Initialize references to matrix element in struct

I have a struct that represents a 3D position. Sometimes it's convenient to access the individual components and sometimes it's convenient to access all components as a vector (physics vector not std::vector) for which I'm using the Eigen linear algebra library. Since there are only three elements (x, y, z) and will only ever be three elements, is there anything wrong with the struct having three double& that refer to the elements of the Eigen Matrix? i.e.:
using ColumnVector3 = Eigen::Matrix<double, 3, 1>;
struct EnuPosition
{
EnuPosition(): pos(ColumnVector3::Zero()), east(pos[0]), north(pos[1]), up(pos[2]) {}
EnuPosition(double east, double north, double up): pos((ColumnVector3() << east, north, up).finished()),
east(pos[0]), north(pos[1]), up(pos[2]) {}
EnuPosition(const ColumnVector3& position): pos(position), east(pos[0]), north(pos[1]), up(pos[2]) {}
EnuPosition(const EnuPosition& enu):pos(enu.pos), east(pos[0]), north(pos[1]), up(pos[2]) {}
EnuPosition& operator=(const EnuPosition& enu)
{
this->pos = enu.pos;
return *this;
}
ColumnVector3 pos;
double& east;
double& north;
double& up;
};
It compiles fine with no warnings on g++ 5.5 with -Wall -Wextra -pedantic in the use cases I can think of:
int main ()
{
EnuPosition enu{12.5, 34.2, 99.2};
std::cout << "east: " << enu.east
<< " north: " << enu.north
<< " up: " << enu.up
<< std::endl;
ColumnVector3 x;
x << 2.0,3.0,4.0;
enu.pos = x;
std::cout << "east: " << enu.east
<< " north: " << enu.north
<< " up: " << enu.up
<< std::endl;
Eigen::MatrixXd y;
y.resize(3,1);
y << 7.6,8.7,9.8;
enu.pos = y;
std::cout << "east: " << enu.east
<< " north: " << enu.north
<< " up: " << enu.up
<< std::endl;
Eigen::Matrix<double,3,3> R;
enu.east = 1;
enu.north = 1;
enu.up = 1;
R << 1,2,3,4,5,6,7,8,9;
enu.pos = (R * enu.pos).eval();
std::cout << "east: " << enu.east
<< " north: " << enu.north
<< " up: " << enu.up
<< std::endl;
EnuPosition enu2 = enu;
std::cout << "east: " << enu2.east
<< " north: " << enu2.north
<< " up: " << enu2.up
<< std::endl;
}
Like I said, it works, I'm just curious if it's legal and not relying on undefined behavior, etc. Or are there other issues to be cognizant of?
After you added the copy-assignment your code should be safe.
However, if you are ok with writing east() instead of east in your code, then a slightly more elegant solution might be this:
using ColumnVector3 = Eigen::Matrix<double, 3, 1>;
struct EnuPosition : public ColumnVector3
{
EnuPosition(): ColumnVector3(ColumnVector3::Zero()) {}
EnuPosition(double east, double north, double up): ColumnVector3(east, north, up) {}
template<class X>
EnuPosition(const X& other): ColumnVector3(other) {}
double& east() {return this->x();}
double const& east() const {return this->x();}
double& north() {return this->y();}
double const& north() const {return this->y();}
double& up() {return this->z();}
double const& up() const {return this->z();}
};
If you intentionally don't want to inherit, you can of course still store the ColumnVector3 as a member.

Resources