std::optional - avoid data copying for RVO
From
David Brown@3:633/10 to
All on Fri Apr 24 14:33:50 2026
I am making some type-safe C++ wrappers for some older C APIs. In one
case I have a C function that is approximately this (returning 1 if the
data was successfully copied) :
extern int try_to_get_data(void *);
and it is used to get data in a simple struct like :
struct Data {
int a;
float b;
char c[40];
};
A simple and efficient wrapper would then be:
bool get1(Data& data) {
return try_to_get_data(&data);
}
That does exactly the same thing, but it type-safe.
However, my preference is that returns from functions are returned,
rather than passing pointers and references around. The natural
signature here is thus to return std::optional<Data>. To possible implementations are :
std::optional<Data> get2() {
if (Data d; try_to_get_data(&d)) return d;
return std::nullopt;
}
std::optional<Data> get3() {
std::optional<Data> so;
if (Data d; try_to_get_data(&d)) so = d;
return so;
}
But these have significant inefficiencies. The local "Data" object
needs to be created on the stack and its address is passed to the
external function - then on success, it is copied from the local stack
object into the caller's stack where the returned std::optional value is
held. On failure, the whole returned std::optional is cleared - not
just the "engaged" boolean inside the std::optional implementation.
I can make my own much-simplified optional type with additional
accessors to get the address of the payload field and to allow directly changing the "engaged" field without affecting the payload. Then I can
have :
Simple get5() {
Simple so;
so.set_engaged(try_to_get_data(&so.value()));
return so;
}
This gives me optimal generated code.
Is there anything I am missing from std::optional that would let me have
this kind of direct access? I suspect not, since that kind of access
bypasses the safety of std::optional of only having access to the
payload if it is engaged. But avoiding the copying makes a big
difference to the efficiency for large optionals.
And is there a good reason for clearing out the payload here when
returning nullopt? Or is that just an inefficiency of the gcc standard
C++ library implementation?
--- PyGate Linux v1.5.14
* Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)