Initialization of list of sets of integers in C++98 - c++

this might seem trivial but I wasn't able to find it on the web so far, so
I want to initialize a list of sets of integers or a set of sets of integers, like this:
list< set<int> > lsi = {{}};
or maybe like this:
set< set<int> > ssi = {{5}, {6}, {7}};
in C++98. The reason for this is that I have to submit it as an exercise in a submission system that does not accept C++11.
I have found out how to initialize a container in the old syntax from an array of integers, for example
int tmp[] = {1,2,3};
set<int> s (tmp, tmp + sizeof(tmp) / sizeof(tmp));
but not a list< set<int> >.

As mentioned in the comments, this is not possible without c+11 / boost.
That being said, the shortest way to initialize would probably be repeating set's initialization (which you mentioned in your question):
int set1v[] = {10, 20, 30, 40};
int set2v[] = {11, 22, 33, 44};
int set3v[] = {15, 25, 35, 45};
set<int> setV[] = {
set<int>(set1v, set1v + sizeof(set1v) / sizeof(set1v[0])),
set<int>(set2v, set2v + sizeof(set2v) / sizeof(set2v[0])),
set<int>(set3v, set3v + sizeof(set3v) / sizeof(set3v[0]))
};
set< set<int> > mySet(setV, setV + sizeof(setV) / sizeof(setV[0]));
Btw, Since you're checking arrays' sizes so many times I'd suggest you use a count macro:
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
It will look a little better:
#define END_OF(x) (x + COUNT_OF(x))
int set1v[] = {10, 20, 30, 40};
int set2v[] = {11, 22, 33, 44};
int set3v[] = {15, 25, 35, 45};
set<int> setV[] = {
set<int>(set1v, END_OF(set1v)),
set<int>(set2v, END_OF(set2v)),
set<int>(set3v, END_OF(set3v))
};
set< set<int> > mySet(setV, END_OF(setV));

I will give you some approach I have used to initialize static STL storages, but the syntax becomes somewhat obscure for this case:
template <typename Type>
struct SetInit : public std::set<Type> {
SetInit<Type>& operator()(const Type& data) {
this->insert(data);
return *this;
}
};
int main () {
std::set<std::set<int> > test_set = SetInit<std::set<int> >() // Call ctor
(SetInit<int>()(1)(2)(3))(SetInit<int>()(4)(5)(6)); // Each element in () is inserted in the set
return 0;
}
The basic idea is to use an operator() to include elements in the storage. The advantage of this idea is that it can be used to initialize static lists and sets because the expressions can be evaluated at compile time.

Related

Is it okay to let a range view own a shared_ptr containing state it needs?

Basically I am trying to get around the fact that the following does not work with range-v3 ranges: (this is a toy example but illustrates the problem.)
namespace rv = ranges::views;
auto bad_foo() {
std::vector<int> local_vector = { 23, 45, 182, 3, 5, 16, 1 };
return local_vector |
rv::transform([](int n) {return 2 * n; });
}
int main() {
for (int n :bad_foo()) {
std::cout << n << " ";
}
std::cout << "\n";
return 0;
}
The above doesn't work because we are returning a view that references data that will go out of scope when bad_foo returns.
What I want to do is store the vector in a shared pointer in such a way that the range view will keep the shared pointer alive. Note that the obvious version of this idea does not work i.e.
auto still_bad_foo() {
auto shared_vector = std::make_shared<std::vector<int>>(
std::initializer_list{ 23, 45, 182, 3, 5, 16, 1 }
);
return *shared_vector |
rv::transform([](int n) {return 2 * n; });
}
still fails because shared_vector is not actually getting captured by the range view. What we want to do is force the range view to own the shared pointer.
The following seems to work to me.
auto good_foo() {
auto shared_vector = std::make_shared<std::vector<int>>(
std::initializer_list{ 23, 45, 182, 3, 5, 16, 1 }
);
return rv::single(shared_vector) |
rv::transform([](auto ptr) { return rv::all(*ptr); }) |
rv::join |
rv::transform([](int n) {return 2 * n; });
}
We use single to turn the shared pointer into a single item range view, turn the single item into a range of ints by dereferencing the pointer in transform and wrapping the vector in a range with all, yielding a composition of range views, which we then flatten via join.
My question is is the above really safe and if so is there a less verbose version of the same idea?
Edit: updated the question to just be about range-v3 ranges as #康桓瑋 has alerted me that this issue is not a problem with C++20 ranges as long as it is not a requirement for the owning range views to be copyable.
Basically I am trying to get around the fact that the following does
not work: (this is a toy example but illustrates the problem. I'm
using range-v3 but this question applies to standard ranges too)
If you are using the standard <ranges> then you don't have to worry about this, just move the vector into the pipe, which will move the ownership of the vector into owning_view
auto not_bad_foo() {
std::vector<int> local_vector = { 23, 45, 182, 3, 5, 16, 1 };
return std::move(local_vector) | // <- here
std::views::transform([](int n) {return 2 * n; });
}
Demo
How about capturing the vector in a lambda to extend its lifetime?
auto create_foo() {
std::vector<int> local_vector = { 23, 45, 182, 3, 5, 16, 1 };
return [captured_vector = std::move(local_vector)]() {
return captured_vector | rv::transform([](int n) {return 2 * n; });
};
}
int main() {
for (int n : create_foo()()) {
std::cout << n << " ";
}
std::cout << "\n";
}

Unite elements that share the same value of a variable in a vector of structs

For example, I have this struct :
struct Time
{
char Day[10];
int pay;
int earn;
}
And suppose that the vector of this Time struct has the following elements:
vector<Time> mySelf = ({"Monday", 20, 40}, {"Tuesday", 15, 20}, {"Monday", 30, 10}, {"Tuesday", 10, 5});
So is there any algorithm to unite the data so that elements with the same day name will appear once and the other variables of those elements will combine together to form a new vector like this :
vector<Time> mySelf = ({"Monday", 50, 50}, {"Tuesday", 25, 25});
You can try to insert your elements to unordered_map, and then reconstruct a vector. Search and insertion to the map have constant-time complexity, so all the operation will be O(n), because we need to iterate over a vector twice.
std::unordered_map<std::string, Time> timeMap;
for (const auto& t : mySelf)
{
if (timeMap.count(t.day) == 0)
{
timeMap[t.day] = t;
}
else
{
timeMap[t.day].pay += t.pay;
timeMap[t.day].earn += t.earn;
}
}
or shorter version, since insert already checks if the element exists and will not overwrite it:
for (const auto& t : mySelf)
{
timeMap.insert({t.day, {t.day,0,0}});
timeMap[t.day].pay += t.pay;
timeMap[t.day].earn += t.earn;
}
and then the vector reconstruction:
std::vector<Time> result;
result.reserve(timeMap.size());
for (const auto&[key, val] : timeMap)
{
result.push_back(val);
}
Alternatively you could use std::unordered_set but then you need some hash function for your struct. Probably you could improve it further with move semantics.
live demo

Update map in-place

I have a piece of code manipulating a map<int,int> object. Let's say that at some point I want to double all the entries, keys and values. Is a there a way to do it in place, without creating a new map, looping and entering updated entries?
My concern is saving memory space.
Doubling the values is possible like this:
std::map<int,int> my_map;
for(auto& item:my_map){
item.second*=2; // double all values
}
However, it is not possible to double the Key since item is from the type std::pair<const int,int>. Notice the const for the Key.
Suggestion:
I think std::map is not best container for this case. Try this way:
std::vector<std::pair<int,int>> my_simi_map;
for(auto& my_simi_map){
item.first*=2; // double all keys
item.second*=2; // double all values
}
Edit:
My concern is saving memory space.
If it is just memory problem then you can pick an item from the map and insert a doubled version of it in the new map and directly delete it from the old map. In this case you will loose the size of just one element of the map not a whole another map.
You can traverse the map in reverse order of key for positive keys, in order to circumvent side effects, and create the new key/value pairs. You can do the equivalent for negative keys.
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
int main() {
map<int, int> m = {
{10, 20},
{-5, 23},
{-10, 7},
{20, 30},
{15, 21},
{18, 2},
};
for (auto it = m.begin(); it != m.end() && it->first < 0; it = m.erase(it)) {
m[it->first * 2] = 2 * it->second;
}
for (auto it = m.rbegin(); it != m.rend() && it->first > 0; it++) {
m[it->first * 2] = 2 * it->second;
m.erase(----it.base());
}
for (auto &p: m) {
printf("%d, %d\n", p.first, p.second);
}
}
Output:
-20, 14
-10, 46
20, 40
30, 42
36, 4
40, 60

C++ : Creating an array of objects

I have the following class:
class student
{
int rol , marks;
char name[20];
public:
student(int r , int m , char n[])
{
rol = r;
marks = m;
strcpy(name,n);
}
int roll()
{
return rol;
}
};
and now I am trying to create an array of objects like so:
student a[]= {(1,10,"AAA"),(2,20,"BBB"),(3,30,"CCC")}; // I get the error on this line
but I get this error message:
Error: testing.cpp(40,56):Cannot convert 'char *' to 'student[]'
When I do this :
student a(1,10,"AAAA");
student b(2,20,"BBBB");
student c(3,30,"CCCC");
student d[3]={a,b,c};
it works perfectly.
#WhozCraig Thx a lot . this is the solution to my problem:
I had to initialize the array as below:
student a[]= {
student(1, 10, "AAA"),
student(2, 20, "BBB"),
student(3, 30, "CCC")
};
My initial code is wrong probably because the constructor cant create more than 1 object at a time.
In an expression (1,10,"AAA") means to apply the comma operator. To initialize an array, you have to provide expressions that can initialize each array member. So one way is:
student a[] = {
student(1, 10, "AAA"), // creates a temporary student to use as initializer
student(2, 20, "BBB"),
student(3, 30, "CCC") };
Since C++11 you can write:
student a[] = { {1, 10, "AAA"}, {2, 20, "BBB"}, {3, 30, "CCC"} };
because C++11 added the feature that an object's constructor can be called via a brace-enclosed initializer list. This is the same reason you could also write afterwards:
a[0] = { 4, 40, "DDD" };
Note: as mentioned in comments, char n[] should be char const n[], and you could improve safety by using std::string name; instead of char name[20];.

What is the easiest way to set the value of an entire array?

My current project requires me to fill an array based upon some other values. I know there's the shortcut:
int arr[4][4] = { {0,0,0,0} , {0,0,0,0} , {0,0,0,0} , {0,0,0,0} };
But in this case, I need to fill the array after its declaration. I currently have my code formatted like this:
int arr[4][4];
if(someothervariable == 1){
arr = { {1,1,1,1},
{1,2,3,4},
{2,,3,4,5},
{3,4,5,6} };
}
But it won't compile. Is there a way to make use of the mentioned shortcut in my case? If not, whats the best fix available? I'd appreciate a way to set it without explicitly assigning each element? ie: arr[0][0] = ...
How about using std::copy() ?
int arr[4][4];
if(someothervariable == 1){
const static int a2[4][4] = { {1,1,1,1},
{1,2,3,4},
{2,3,4,5},
{3,4,5,6} };
std::copy(&a2[0][0], &a2[0][0]+16, &arr[0][0]);
}
No, array initialization syntax is for array initialization. Although, you can use memset if all the values are the same byte.
The boost.assign library adds some interesting syntax for modifying/filling collections, but AFAIK it doesn't support C style arrays (only C++ and Boost containers).
In the current version of C++ language the only way to do it is to copy it from some original
int arr[4][4];
if (someothervariable == 1)
{
const int SOURCE[4][4] = // make it `static` if you prefer
{
{1, 1, 1, 1},
{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6}
};
assert(sizeof arr == sizeof SOURCE); // static assert is more appropriate
memcpy(&arr, &SOURCE, sizeof arr);
}
The source "constant" can be declared as static in order to avoid re-initialization, if the compiler is not smart enough to optimize it by itself.
In the future version of the language a feature similar to C's compound literals is planned, which will provide support for immediate initialization (basically what you tried to do in your original post).
If you wish to fill the array with a single value:
#include<algorithm>
#include<vector>
// ...
std::vector<int> arr;
std::fill(arr.begin(), arr.end(), VALUE); // VALUE is an integer
If you wish to calculate the value for each element:
struct get_value {
int operator()() const { /* calculate and return value ... */ }
};
std::generate(arr.begin(), arr.end(), get_value());
If you are setting everything to the same value (such as zero), you may be able to get away with ...
memset (arr, 0, sizeof (arr));
Note that this is fraught with perils. You have to know your type sizes and all that jazz.
However, it appears that that will not suffice for you. If you want to fill the array with different values, I can only only think of two ways of doing this.
Method #1. (Can be a pain the butt)
arr[0][0] = 1;
...
arr[0][3] = 1;
arr[1][0] = 1;
...
arr[1][3] = 4;
arr[2][0] = 2;
...
arr[2][3] = 5;
arr[3][0] = 3;
...
arr[3][3] = 6;
Method #2.
Predefine a set of arrays and switch between them using a pointer;
int arr1[4][4] = {
{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0} };
int arr2[4][4] = {
{1,1,1,1},
{1,2,3,4},
{2,,3,4,5},
{3,4,5,6} };
int *arr[4];
Now you only have the four (4) values of *arr[] to set instead of setting everything. Of course, this really only works if your arrays will be filled with predetermined constants.
Hope this helps.
I'm not sure if I like this solution or not, but C/C++ will give you assignment convenience if you wrap the array inside a struct with the minor cost of then having to use the struct name to get at the array:
typedef struct {
int data[4][4];
} info_t;
info_t arr;
if (someothervariable == 1){
static const info_t newdata = {{ // since this is static const, there generally
// won't be a copy - that data will be 'baked'
// into the binary image (or at worst a
// single copy will occur)
{1,1,1,1},
{1,2,3,4},
{2,3,4,5},
{3,4,5,6}
}};
arr = newdata; // easy to assign new data to the array
}
int somethingelse = arr.data[1][2]; // a tiny bit less convenient to get
// to the array data
int arr[4][4];
if (someothervariable == 1) {
int tmp[4][4] = { {1, 1, 1, 1}, {1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6} };
arr = tmp;
}