Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to take QByteArray from std::optional<QByteArray> and leave std::nullopt without additional mallocs?

Tags:

c++

c++17

qt

I need to write a function that takes ownership of an optional<QByteArray>'s underlying array.

In Rust, it is done by this way:

fn my_func(f: &mut Option<Box<[u8]>>) -> Box<[u8]> {
     //how to do "take" in c++17?
     f.take().unwrap()
}

I know about std::move, but as far as I understand, this code will malloc data for a new array, swap pointers, and drop unneeded QByteArray:

std::optional<QByteArray> optarr = ...
QByteArray optarr = std::move(*myopt);
optarr.reset()

Is there any way to do this as well as in Rust (that is, without allocating memory for temporary QByteArray)?

like image 666
chabapok Avatar asked Dec 17 '25 12:12

chabapok


2 Answers

The equivalent function in C++ would be:

auto func(optional<QByteArray>&& o) -> QByteArray
{
    return std::move(o).value();
}

std::move(o) gives you an rvalue of type optional<QByteArray>. Invoking value() on that rvalue gives you a QByteArray&& (or throws if o is disengaged). That rvalue of type QByteArray is used to move construct the return object. This move construction is cheap, just a few pointer assignments - no memory allocation happens.


Almost equivalent. Note that o here does not end up as a disengaged optional, unlike in Rust. It is still engaged, just holding an empty QByteArray. Truly equivalent, if really desired, would be:

auto func(optional<QByteArray>&& o) -> QByteArray
{
    QByteArray result = std::move(o).value(); // NB: not a reference
    o.reset();                                // or '= nullopt', or '= {}'
    return result;                            // NB: no need to move
}
like image 129
Barry Avatar answered Dec 19 '25 01:12

Barry


Your assumption about std::move is wrong. Roughly speaking std::move casts to an rvalue reference, no actual code is generated in itself by that. The following line will create a QByteArray on the stack by calling its move constructor.

QByteArray optarr = std::move(*myopt);

The move constructor initializes the Data pointer from the other QByteArray and replaces the other's data pointer with pointer to shared empty data. No malloc is involved, just two pointer assignments.

The implementation of the move constructor looks like this:

inline QByteArray(QByteArray && other) Q_DECL_NOTHROW : d(other.d) { other.d = Data::sharedNull(); }

So your code is actually doing what you want.

like image 37
PaulR Avatar answered Dec 19 '25 02:12

PaulR



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!