C++ nested designated initializer with pointer

Viewed 292

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?

2 Answers

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
        })
    })
});
Related