Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ nested designated initializer with pointer

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?

like image 324
akarin64 Avatar asked Jun 19 '21 20:06

akarin64


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. ...

like image 76
eerorika Avatar answered Oct 27 '22 01:10

eerorika


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
        })
    })
});
like image 36
Yakk - Adam Nevraumont Avatar answered Oct 26 '22 23:10

Yakk - Adam Nevraumont