3

I am trying to elegantly declare a constant std::set object that would be a merger of two other constant std::set objects.

#include <set>

const std::set<int> set_one = { 1,2,3 };
const std::set<int> set_two = { 11,15 };
const std::set<int> set_all = { 1,2,3,11,15 }; // this is not very elegant, duplication

Declaring set_all object this way is not too elegant, as it duplicates information from the previous two lines. Is there a way to use set_one and set_two constants in declaring set_all?

Something like this:

const std::set<int> set_all = set_one + set_two; // this does not compile, of course!
  1. All objects are strictly constants.
  2. There are no overlapping values in both source sets, so uniqueness will not be an issue.
  3. I know how to merge sets in runtime, this is not what I am looking for.
  4. I am really trying to avoid resorting to macros like this:
#include <set>

#define SET_ONE 1, 2, 3
#define SET_TWO 11, 15

const std::set<int> set_one = { SET_ONE };
const std::set<int> set_two = { SET_TWO };
const std::set<int> set_all = { SET_ONE, SET_TWO };
JeJo
  • 30,635
  • 6
  • 49
  • 88
Regus Pregus
  • 560
  • 3
  • 12
  • 2
    None of your `std::set`s are created during the compile time (they are all created during runtime, because they allocate). – Fureeish Jul 08 '20 at 17:58
  • 1
    `std::set` uses dynamic allocation. That means it is created at run time, well at least until possibly C++20. – NathanOliver Jul 08 '20 at 17:58

1 Answers1

6

You can pack them into a lambda and call it immediately (i.e. IIFE).

const std::set<int> set_all = [&set_one, &set_two]() {
   std::set<int> set{ set_one.cbegin(),set_one.cend() };
   set.insert(set_two.cbegin(), set_two.cend());
   return set;
}(); // ---> call the lambda!

However, if you have the sets in the global scope(like @Kevin mentioned), you should use the lambda which takes the two sets as arguments

#include <set>

using Set = std::set<int>;    // type alias
const Set set_one = { 1,2,3 };
const Set set_two = { 11,15 };

const Set set_all = [](const Set& setOne, const Set& setTwo)
{
   Set set{ setOne.cbegin(), setOne.cend() };
   set.insert(setTwo.cbegin(), setTwo.cend());
   return set;
}(set_one, set_two); // ---> call the lambda with those two sets!

or simply

const std::set<int> set_all = []()
{
   std::set<int> set{ set_one.cbegin(),set_one.cend() };
   set.insert(set_two.cbegin(), set_two.cend());
   return set;
}(); // ---> call the lambda!

I know how to merge sets in runtime, this is not what I am looking for.

No, You can not create the std::set at compile time as it uses dynamic allocation. Therefore, everything happens at run-time. Even the above lambda.

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Remove the capture (`&`) if these sets are global variables (as seems to be the case in OP's code) – Kevin Jul 08 '20 at 18:00
  • 1
    you should clarify that this is not "not at runtime" – 463035818_is_not_an_ai Jul 08 '20 at 18:01
  • @Kevin why would you assume that? That's a rare corner case. It's not worth to list every one of them and, if we don't want to list them, why mention this specific one? – Fureeish Jul 08 '20 at 18:01
  • @Fureeish Because of this error: `error: non-local lambda expression cannot have a capture-default` (and if you try to capture individually: `'set_one' cannot be captured because it does not have automatic storage duration`) – Kevin Jul 08 '20 at 18:03
  • @Kevin imo that's saying "if you do this weird stuff, which you shouldn't have done at the first place, then the code above has to be slightly changed". I do not believe adding such corner cases to answers is beneficial in any way. – Fureeish Jul 08 '20 at 18:05
  • @Fureeish I wouldn't have said anything about it if the OP's code didn't have the sets as global variables. But I think it's important to mention corner cases if that's precisely what the OP is using. – Kevin Jul 08 '20 at 18:08
  • 2
    @Kevin OP gave us just a portion of code and they didn't mention anything about globals. I think that should settle it. – Fureeish Jul 08 '20 at 18:09
  • @Fureeish OP gave us a portion of code directly below `#include `, so at least in that code those variables *are* global. Considering these sets are also `const`, I don't see a reason to dismiss the idea that OP might *actually* be using globals. – Kevin Jul 08 '20 at 18:15
  • Why add unused parameters to the lambda? Removing the captures should be enough. – HolyBlackCat Jul 08 '20 at 18:42