Update map in-place - c++

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

Related

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

MultiIndex containers -- offering vector and set access

I have an application where, first, an std::vector<int> object is generated. Then, some operations need to be performed on this object viewed as an std::set<int> where the order does not matter and repetitions don't count.
At present, I explicitly construct an object of type std::set<int> from the std::vector<int> object. An example is presented below:
#include <cstdio>
#include <set>
#include <vector>
void printset(std::set<int>& Set) {
printf("Printing Set Elements: ");
for (std::set<int>::iterator siter = Set.begin(); siter != Set.end(); ++siter) {
int val = *siter;
printf("%d ", val);
}
printf("\n");
}
void printvec(std::vector<int>& Vec) {
printf("Printing Vec Elements: ");
for (size_t i = 0, szi = Vec.size(); i < szi; ++i) {
int val = Vec[i];
printf("%d ", val);
}
printf("\n");
}
int main()
{
std::vector<int> VecInt{ 6, 6, 5, 5, 4, 4 };
std::set<int> SetInt(VecInt.begin(), VecInt.end());
printvec(VecInt);
printset(SetInt);
}
I am trying to see if I can use Boost.MultiIndex for this purpose. One introduction to Boost.MultiIndex states:
Boost.MultiIndex can be used if elements need to be accessed in different ways and would normally need to be stored in multiple containers. Instead of having to store elements in both a vector and a set and then synchronizing the containers continuously, you can define a container with Boost.MultiIndex that provides a vector interface and a set interface.
and this is precisely what I am doing (using multiple containers and then creating one from the other constantly) while I would like to create a (multi-index) container once and then provide a vector interface and a set interface.
On looking through various examples, for e.g., here and here, it is not clear how those examples can be modified to the code example above.
Ideally, I would like to do the following in the code example above:
MultiIndexContainer vec_set_container;
vec_set_container.push_back(6);//or anything equivalent for the MultiIndexContainer
vec_set_container.push_back(6);
vec_set_container.push_back(5);
vec_set_container.push_back(5);
vec_set_container.push_back(4);
vec_set_container.push_back(4);
printvec(vec_set_container.Some_Function_That_Exposes_Vector_Interface());
printset(vec_set_container.Some_Function_That_Exposes_Set_Interface());
How can this be accomplished using Boost.MultiIndex ?
Random access index would match the "vector" interface.
An ordered unique index would match the "set" interface.
However, if you have a unique index, this will prevent insertion of duplicates. So, you would get:
Live On Compiler Explorer
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index_container.hpp>
#include <fmt/ranges.h>
namespace bmi = boost::multi_index;
using Table = bmi::multi_index_container< //
int,
bmi::indexed_by<
bmi::random_access<bmi::tag<struct asVector>>,
bmi::ordered_unique<bmi::tag<struct asSet>, bmi::identity<int>>>>;
int main()
{
Table data{ 6, 6, 5, 5, 4, 4 };
fmt::print("As vec {}\nAs set {}\n", //
data.get<asVector>(), //
data.get<asSet>());
}
Printing
As vec {6, 5, 4}
As set {4, 5, 6}
Now, I think the "best" you could do with this is to make the order index non-unique (so, mimicking a std::multiset instead of std::set): Live On Compiler Explorer
bmi::ordered_non_unique<bmi::tag<struct asSet>, bmi::identity<int>>
Printing
As vec [6, 6, 5, 5, 4, 4]
As set {4, 4, 5, 5, 6, 6}
If you want to traverse unique elements, using a range adaptor would be minimally costly:
Using Boost Live
fmt::print("As vec {}\nAs set {}\n", //
data.get<asVector>(), //
data.get<asSet>() | boost::adaptors::uniqued);
Using RangeV3 Live
fmt::print("As vec {}\nAs set {}\n", //
data.get<asVector>(), //
data.get<asSet>() | ranges::views::unique);
Using Standard Ranges; I couldn't make this work but it should really be something like std::ranges::unique(data.get<asSet>())
All printing
As vec {6, 6, 5, 5, 4, 4}
As set {4, 5, 6}
Other Ideas
If you don't require random access, then sequenced index might be preferrable for you. And note that this interface comes with the handy unique() and sort() methods (just like std::list).
UPDATE To The Comments
Here's a rolled-in-one response to the comments:
Live On Compiler Explorer
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
#include <fmt/ranges.h>
#include <random>
namespace bmi = boost::multi_index;
template <typename T>
using ListSet = bmi::multi_index_container< //
T,
bmi::indexed_by<
bmi::sequenced<bmi::tag<struct byInsertion>>, //
bmi::ordered_unique<bmi::tag<struct Ordered>, bmi::identity<T>> //
>>;
int main()
{
ListSet<int> data;
std::mt19937 prng{99}; // "random" seed
for (std::uniform_int_distribution d(1, 10); data.size() < 5;) {
int el = d(prng);
if (auto [it, unique] = data.push_back(el); not unique) {
fmt::print("Element {} duplicates index #{}\n", el,
std::distance(begin(data), it));
}
}
fmt::print("By insertion {}\nOrdered {}\n", data, data.get<Ordered>());
}
Prints
Element 9 duplicates index #3
Element 8 duplicates index #1
By insertion [7, 8, 5, 9, 1]
Ordered {1, 5, 7, 8, 9}

Order a list depending on other list

Given:
struct Object {
int id;
...
};
list<Object> objectList;
list<int> idList;
What is the best way to order objectList depending on order of idList?
Example (pseudo code):
INPUT
objectList = {o1, o2, o3};
idList = {2, 3, 1};
ACTION
sort(objectList, idList);
OUTPUT
objectList = {o2, o3, o1};
I searched in documentation but I only found methods to order elements comparing among themselves.
You can store the objects in an std::map, with id as key. Then traverse idList, get the object out of map with its id.
std::map<int, Object> objectMap;
for (auto itr = objectList.begin(); itr != objectList.end(); itr++)
{
objectMap.insert(std::make_pair(itr->id, *itr));
}
std::list<Object> newObjectList;
for (auto itr = idList.begin(); itr != idList.end(); itr++)
{
// here may fail if your idList contains ids which does not appear in objectList
newObjectList.push_back(objectMap[*itr]);
}
// now newObjectList is sorted as order in idList
Here is another variant, which works in O(n log n). This is asymptotcally optimal.
#include <list>
#include <vector>
#include <algorithm>
#include <iostream>
#include <cassert>
int main() {
struct O {
int id;
};
std::list<O> object_list{{1}, {2}, {3}, {4}};
std::list<int> index_list{4, 2, 3, 1};
assert(object_list.size() == index_list.size());
// this vector is optional. It is needed if sizeof(O) is quite large.
std::vector<std::pair<int, O*>> tmp_vector(object_list.size());
// this is O(n)
std::transform(begin(object_list), end(object_list), begin(tmp_vector),
[](auto& o) { return std::make_pair(o.id, &o); });
// this is O(n log n)
std::sort(begin(tmp_vector), end(tmp_vector),
[](const auto& o1, const auto& o2) {
return o1.first < o2.first;
});
// at this point, tmp_vector holds pairs in increasing index order.
// Note that this may not be a contiguous list.
std::list<O> tmp_list(object_list.size());
// this is again O (n log n), because lower_bound is O (n)
// we then insert the objects into a new list (you may also use some
// move semantics here).
std::transform(begin(index_list), end(index_list), begin(tmp_list),
[&tmp_vector](const auto& i) {
return *std::lower_bound(begin(tmp_vector), end(tmp_vector),
std::make_pair(i, nullptr),
[](const auto& o1, const auto& o2) {
return o1.first < o2.first;
})->second;
});
// As we just created a new list, we swap the new list with the old one.
std::swap(object_list, tmp_list);
for (const auto& o : object_list)
std::cout << o.id << std::endl;
}
I assumed that O is quite large and not easily movable. Therefore i first create tmp_vector which only contains of pairs. Then I sort this vector.
Afterwards I can simply go through the index_list and find the matching indices using binary search.
Let me elaborate on why a map is not the best solution eventhough you get a quite small piece of code. If you use a map you need to rebalance your tree after each insertion. This doesn't cost asympatotically (because n times rebalancing costs you the same as sorting once), but the constant is way larger. A "constant map" makes not that much sense (except accessing it may be easier).
I then timed the "simple" map-approach against my "not-so-simple" vector-approach. I created a randomly sorted index_list with N entries. And this is what I get (in us):
N map vector
1000 90 75
10000 1400 940
100000 24500 15000
1000000 660000 250000
NOTE: This test shows the worst case as in my case only index_list was randomly sorted, while the object_list (which is inserted into the map in order) is sorted. So rebalancing shows all its effect. If the object_list is kind of random, performance will behave more similar, eventhough performance will always be worse. The vector list will even behave better when the object list is completely random.
So already with 1000 entries the difference is already quite large. So I would strongly vote for a vector-based approach.
Assuming the data is handled to you externally and you don't have the choice of the containers:
assert( objectList.size() == idList.size() );
std::vector<std::pair<int,Object>> wrapper( idList.size() );
auto idList_it = std::begin( idList );
auto objectList_it = std::begin( objectList );
for( auto& e: wrapper )
e = std::make_pair( *idList_it++, *objectList_it++ );
std::sort(
std::begin(wrapper),
std::end(wrapper),
[]
(const std::pair<int,Object>& a, const std::pair<int,Object>& b) -> bool
{ return a.first<b.first; }
);
Then, copy back to original container.
{
auto objectList_it = std::begin( objectList );
for( const auto& e: wrapper )
*objectList_it++ = e;
}
But this solution is not optimal, I'm sure somebody will come with a better solution.
Edit: The default comparison operator for pairs requires that it is defined both for first and second members. Thus the easiest way is to provide a lambda.
Edit2: for some reason, this doesn't build if using a std::list for the wrapper. But it's ok if you use a std::vector (see here).
std::list has a sort member function you can use with a custom comparison functor.
That custom functor has to look up an object's id in the idList and can then use std::distance to calculate the position of the element in idList. It does so for both objects to be compared and returns true if the first position is smaller than the second.
Here is an example:
#include <iostream>
#include <list>
#include <algorithm>
#include <stdexcept>
struct Object
{
int id;
};
int main()
{
Object o1 = { 1 };
Object o2 = { 2 };
Object o3 = { 3 };
std::list<Object> objectList = { o1, o2, o3 };
std::list<int> const idList = { 2, 3, 1 };
objectList.sort([&](Object const& first, Object const& second)
{
auto const id_find_iter1 = std::find(begin(idList), end(idList), first.id);
auto const id_find_iter2 = std::find(begin(idList), end(idList), second.id);
if (id_find_iter1 == end(idList) || id_find_iter2 == end(idList))
{
throw std::runtime_error("ID not found");
}
auto const pos1 = std::distance(begin(idList), id_find_iter1);
auto const pos2 = std::distance(begin(idList), id_find_iter2);
return pos1 < pos2;
});
for (auto const& object : objectList)
{
std::cout << object.id << '\n';
}
}
It's probably not terribly efficient, but chances are you will never notice. If it still bothers you, you might want to look for a solution with std::vector, which unlike std::list provides random-access iterators. That turns std::distance from O(n) to O(1).
I would find it strange to end up in this situation as I would use the pointers instead of the ids. Though; there might be usecases for this.
Note that in all examples below, I assume that the ids-list contains all ids exactly ones.
Writing it yourself
The issue you like to solve is creating/sorting a list of objects based on the order of the ids in another list.
The naive way of doing this, is simply writing it yourself:
void sortByIdVector(std::list<Object> &list, const std::list<int> &ids)
{
auto oldList = std::move(list);
list = std::list<Object>{};
for (auto id : ids)
{
auto itElement = std::find_if(oldList.begin(), oldList.end(), [id](const Object &obj) { return id == obj.id; });
list.emplace_back(std::move(*itElement));
oldList.erase(itElement);
}
}
If you use a sorted vector as input, you can optimize this code to get the best performance out of it. I'm leaving it up-to you to do so.
Using sort
For this implementation, I'm gonna assume this are std::vector instead of std::list, as this is the better container to request the index of an element. (You can with some more code do the same for list)
size_t getIntendedIndex(const std::vector<int> &ids, const Object &obj)
{
auto itElement = std::find_if(ids.begin(), ids.end(), [obj](int id) { return id == obj.id; });
return itElement - ids.begin();
}
void sortByIdVector(std::list<Object> &list, const std::vector<int> &ids)
{
list.sort([&ids](const Object &lhs, const Object &rhs){ return getIntendedIndex(ids, lhs) < getIntendedIndex(ids, rhs); });
}
Insertion
Another approach, also more suitable for std::vector would be simply inserting the elements at the right place and will be more performant than the std::sort.
void sortByIdVector(std::vector<Object> &list, const std::vector<int> &ids)
{
auto oldList = std::move(list);
list = std::vector<Object>{};
list.resize(oldList.size());
for (Object &obj : oldList)
{
auto &newLocation = list[getIntendedIndex(ids, obj)];
newLocation = std::move(obj);
}
}
objectList.sort([&idList] (const Object& o1, const Object& o2) -> bool
{ return std::find(++std::find(idList.begin(), idList.end(), o1.id),
idList.end(), o2.id)
!= idList.end();
});
The idea is to check if we find o1.id before o2.id in the idList.
We search o1.id, increment the found position then we search o2.id: if found, that implies o1 < o2.
Test
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
struct Object {
int id;
string name;
};
int main()
{
list<Object> objectList {{1, "one_1"}, {2, "two_1"}, {3, "three_1"}, {2, "two_2"}, {1, "one_2"}, {4, "four_1"}, {3, "Three_2"}, {4, "four_2"}};
list<int> idList {3, 2, 4, 1};
objectList.sort([&idList] (const Object& o1, const Object& o2) -> bool
{ return std::find(++std::find(idList.begin(), idList.end(), o1.id), idList.end(), o2.id) != idList.end(); });
for(const auto& o: objectList) cout << o.id << " " << o.name << "\n";
}
/* OUTPUT:
3 three_1
3 Three_2
2 two_1
2 two_2
4 four_1
4 four_2
1 one_1
1 one_2
*/

How to compare two non-random access iterator in C++

I have this :
vector<int> vec = {10, 4, 18, 7, 2, 10, 25, 30};
auto& pos25 = find(vec.cbegin(), vec.cend(), 25);
auto& pos18 = find(vec.cbegin(), vec.cend(), 18);
Now, i want to make a query to search for 7 between there two positions. I can just use operator< between pos25 and pos18 since they're random acess iterators and then I can find the location of 7 within that range.
But what if my container is a forward_list. How will I implement that since I don't have an operator< to compare these two iterators; hence I can't know whether pos25 or pos18 occur first to give a range to the find function.
I found this method in a book :
pos18 = find (vec.begin(), vec.end(), // range
18); // value
pos25 = find (vec.begin(), pos18, // range
25); // value
if (pos18 != coll.end() && pos25 != pos18) {
// pos25 is in front of pos18
// so, only [pos25,pos18) is valid
...
}
else {
pos25 = find (pos25, vec.end(), // range
25); // value
if (pos25 != coll.end()) {
// pos18 is in front of pos25
// so, only [pos18,pos25) is valid
...
}
else {
// 18 and/or 25 not found
...
}
}
Though this is simple enough, is there anything more efficient?
Iterating over a linked list is of relatively high cost because of the potential accesses to memory that must be made. You'll want to minimize those accesses. There are a couple things you could do to that end:
Use find_if to search for either 18 or 25
Then search from that point with find_if again for either 7 or the other bound
If the other bound was found 1st there is no intervening 7 if the 7 was found first ensure the other bound exists
So your code could look like this:
const auto start = find_if(cbegin(vec), cend(vec), [](const auto& i){ return i == 18 || i == 25; });
const auto target = find_if(start, cend(vec), [finish = *start == 18 ? 25 : 18](const auto& i){ return i == 7 || i == finish; });
const auto finish = *target == 7 ? find(target, cend(vec), *start == 18 ? 25 : 18) : cend(vec);
After this if finish doesn't point to cend(vec) then target is a valid pointer to the 1st 7 in the range.
Live Example
Vlad from Moscow's solution cleverly avoided the need for lambdas by using find_first_of, but it iterated over the contents of vec more than once, making it more expensive than my algorithm. The marriage of these 2 algorithms results in an algorithm that is faster than my original while preserving the benefit of only accessing each element once:
const int a[] = { 18, 25 };
const auto start = find_first_of(cbegin(vec), cend(vec), cbegin(a), cend(a));
const int b[] = { *start == *cbegin(a) ? *crbegin(a) : *cbegin(a), 7 };
const auto target = find_first_of(start, cend(vec), cbegin(b), cend(b));
const auto finish = *target == *crbegin(b) ? find(target, cend(vec), *cbegin(b)) : cend(vec);
Again if finish doesn't point to cend(vec) then target is a valid pointer to the 1st 7 in the range.
Live Example
For starters this code snippet (if to update a typo) is wrong
vector<int> vec = {10, 4, 18, 7, 2, 10, 25, 30};
auto& pos25 = find(vec.cbegin(), vec.cend(), 25);
auto& pos18 = find(vec.cbegin(), vec.cend(), 18);
You may not bind a temporary object with a non-constant reference.
As for this approach
pos18 = find (vec.begin(), vec.end(), // range
18); // value
pos25 = find (vec.begin(), pos18, // range
25); // value
then you will need to check many conditions. For example before calling
pos25 = find (vec.begin(), pos18, // range
25); // value
you should check whether pos19 is not equal to vec.end().
As for determining which iterator is less or greater than you can use standard function std::distance. However it is inefficient applied to iterators of the container std::forward_list.
A more efficient approach is to use standard algorithm std::find_first_of instead of the algorithm std::find to find the first iterator of the range.
Here is a demonstrative program
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> vec = { 10, 4, 18, 7, 2, 10, 25, 30 };
int a[] = { 25, 18 };
auto first = std::find_first_of(vec.cbegin(), vec.cend(),
std::begin(a), std::end(a));
auto last = vec.cend();
auto target = vec.cend();
if (first != vec.cend())
{
last = std::find(first, vec.cend(),
*first == a[0] ? a[1] : a[0]);
}
if (last != vec.cend())
{
target = std::find(first, last, 7);
}
if (target != vec.end())
{
std::cout << 7 << " is between " << *first
<< " and " << *last << std::endl;
}
}
The program output is
7 is between 18 and 25
You don't have any options to compare two ForwardIterator's other than operator ==. This means that you have only two ways here:
Use std::find to ensure the iterators belong to a particular part of your array. This method is implemented in code you cited and in the #Jonathan's answer.
Compare two iterators by writing a special procedure for such operations.
For example, you can write the following code:
template<typename ForwardIterator>
bool less(ForwardIterator lhs, ForwardIterator rhs, ForwardIterator end) {
if (lhs == rhs) {
return false; // Equal
}
while (lhs++ != end) {
if (lhs == rhs) {
return true; // rhs is after lhs
}
}
return false; // lhs is after rhs
}
Please also note that this procedure assumes that both iterators belong to the same container and has linear time complexity.
Personally, I would recommend in such situation using a RandomAccessIterator. Yes, std::list does not provide one, but you may use std::vector instead.

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

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.