I would like to carry out a lazy initialization of a set of (std::vector) attributes in c++. They have to be const, in the sense that after the first time they are initialized (via a get method), their values cannot be modified. What is the cleanest way to do this?
I tried to define the attributes as const, but in this way they must be initialized in the construntor initialization list, so no lay initialization seems to be possible.
I would like to carry out a lazy initialization of a set of (std::vector) attributes in c++. They have to be const, in the sense that after the first time they are initialized (via a get method), their values cannot be modified. What is the cleanest way to do this?
I tried to define the attributes as const, but in this way they must be initialized in the construntor initialization list, so no lay initialization seems to be possible.
Share Improve this question asked Jan 18 at 14:50 AntonioAntonio 232 bronze badges 3 |3 Answers
Reset to default 3You might do something like:
class MyClass
{
public:
const std::vector<int>& getV()
{
if (!v) {
v = std::vector{4, 8, 15, 16, 23, 42};
}
return *v;
}
private:
std::optional<std::vector<int>> v;
};
If some specific value is "prohibited" from vector (as empty vector), you might remove optional and use that value instead.
You may use the "Meyer's singleton" pattern by using a static in the getter itself. This give the guaranty to be initialized on first access (and is even thread safe)
struct MyClass
{
const std::vector<int>& getV()
{
static auto v = std::vector{4, 8, 15, 16, 23, 42};
return v;
}
};
full working example there => https://godbolt./z/8jheTq1WT
Proper handling of Lazy initialization needs language support that is currently unavailable in C++. But using the return value of functions is a possibility. Therefore, we can use the IILE(immediately invoked lambda expression) pattern:
auto const my_lazy_vec =
[& /*capture all by ref*/ ]{
std::vector<elements> nrvo;
{// safety scope for nrvo
if (case_1)
do_fill(nrvo, value_set_1);
else if (case_2)
do_fill(nrvo, value_set_2);
else if (case_3)
do_fill(nrvo, value_set_3);
return nrvo;
};//safety scope for nrvo
}(/*IILE*/);// init my_lazy_vec
In the above snippet nrvo
is not essential, but serves as a good example. Using lambda, makes the initialization logic local and visible - while still conditional and flexible. And the result of immediate evaluation is directly initialized into a const object.
In other words, the mutable stage of object's lifetime is handled inside the lambda, while the immutable part is handled by the return value.
const std::vector<int>& getConstVector() { static std::vector<int> values = immediate_invoked_lambda[]{ ... initialization code here ... }(); return values; }
– Pepijn Kramer Commented Jan 18 at 15:03const
, you want logically constant. Then implement alazy<T>
for your lazily initialized set of attributes. Make thelazy<T>
have a logically constant interface, rather than be aconst
object itself. – Eljay Commented Jan 18 at 15:10vector
cannot be modified in size it's contents can be since they cannot be const. see this – doug Commented Jan 18 at 16:28