Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you initialise "static const vectors" of unique_ptrs? (C++17 with GCC 7.3)

Tags:

c++

gcc

c++17

stl

I'm trying to create a static const list of default objects (Rules) that are to big to be frequently copied and as such I want to store them in a vector of unique_ptr<>. I've noticed that similar questions has made the rounds a few times but I'm unclear as to whether this is actually doable (I'm leaning towards not doable). I.e.

  1. You can't use initializer_list with unique_ptr since access to the members are const resulting in a copy operation.
  2. You can't pass temporaries in by reference resulting in a copy operation.

Thus both:

static const std::vector<std::unique_ptr<std::string>> kStrings = {
  std::unique_ptr<std::string>(new std::string("String 1")),
  std::unique_ptr<std::string>(new std::string("String 1"))
};

and

static const std::vector<std::unique_ptr<std::string>> kStrings(
  std::unique_ptr<std::string>(new std::string("String 1")),
  std::unique_ptr<std::string>(new std::string("String 1"))
);

are not feasible.

Am I misunderstanding? Is there another way to initialize such a vector or is this a case of rethinking the design? The simplest alternative I can think of is to create the objects a static const and then referencing those as raw pointers. For example:

static const std::string kString1 = "String 1";
static const std::string kString2 = "String 2";

static const std::vector<const std::string*> kStrings = {&kString1, &kString2};

It feels nasty, but the code interfaces remains relatively unchanged and since it does not leave the scope of the class it is also transparent to the users.

Any thoughts, corrections or better alternatives is appreciated.

EDIT - Regarding this question, I've tested the solution and it also works. My problem with the solution is it enables initializer_list to used in a way they were not designed to be used and I don't know what unintended consequence that will have, if any.

like image 700
Vinnie Avatar asked Mar 06 '23 20:03

Vinnie


1 Answers

Am I misunderstanding? Is there another way to initialize such a vector

There is, call a function that returns a non-const vector to use as an initializer. It doesn't even have to be named. It could be an immediately invoked lambda.

static const std::vector<std::unique_ptr<std::string>> kStrings = [] {
  std::vector<std::unique_ptr<std::string>> ret;
  // populate ret
  return ret;
}(); // <-- Invoke the lambda immediately after defining it. 

Now you can have your complex initialization logic and treat kStrings as const still. Furthermore, it's quite likely compilers will inline the lambda entirely, so there isn't even going to be the overhead of a function call. It really is having your cake and eating it too.

Though one has to wonder why you'd use a vector of unique pointers to strings (a value type). A vector already owns its content. So you could possibly cut the middleman, and that will simplify initialization as well.

like image 65
StoryTeller - Unslander Monica Avatar answered Apr 07 '23 05:04

StoryTeller - Unslander Monica