I thought it would be useful to be able to initialize a struct that references another struct with a pointer, using the designated initializer. Such a pattern often happens in some APIs (e.g., Vulkan). For instance, consider the following structs:
struct A {
int v;
};
struct B {
int v;
A* a;
};
struct C {
B* b;
};
and a function taking C
as an argument
void func(const C& c) {}
I came up with an approach using unmove()
(ref) function that converts rvalue to lvalue:
template <typename T>
T& unmove(T&& v) { return v; }
Using this function, the nested initialization can be written by
func({
.b = &unmove(B{
.v = 1,
.a = &unmove(A{
.v = 2
})
})
});
My question: Is it a valid usage of the function in terms of the standard? Or am I missing something?
This is safe as long as the function doesn't store or return the referred object or its address or the nested pointers for later usage. The temporary objects will be destroyed at the end of the full expression and as such the mentioned stored / returned references / pointers would be invalid.
[class.temporary]
... Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created. ...
I'd make it addressof_rvalue
:
template <typename T>
T* addressof_rvalue(T&& v) { return std::addressof(v); }
template <typename T>
T* addressof_rvalue(T& v) = delete;
it is safe, so long as the pointer doesn't outlive the function you are calling.
func({
.b = addressof_rvalue(B{
.v = 1,
.a = addressof_rvalue(A{
.v = 2
})
})
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With