c++move-semanticsboost-dynamic-bitsetxvalue

Why do I need to call std::move on a temporary dynamic_bitset?


I'm telling a longwinded backstory here because, in addition to a direct answer, I'd like to know if my reasoning which led into this situation was correct.

I have a function taking a dynamic_bitset<> argument (from Boost.dynamic_bitset). Say it looks like this.

void foo(boost::dynamic_bitset<> db) {
    // do stuff
}

It so happens that it's only ever called with temporaries, built from the constructor, as in foo(boost::dynamic_bitset<>{5}.set()) (to call with a 5-bit bitset with all bits set).

My bitsets only have a small number of bits (fewer than 32). So at first, I thought "I'll just pass it by value; the copy is smaller than a pointer." But then I thought "It's dynamic, so it must allocate space on the heap. I want to avoid unnecessary allocations and frees."

So, I could make it

void foo(const boost::dynamic_bitset<>& db);

But a reference is a pointer, and a dynamic_bitset (presumably) has a pointer to its data, so then using db within foo would go through two levels of indirection, which seems dumb. Clearly the best way would be to copy the pointer to the data into foo, without reallocating and copying the data on the heap.

"Aha!" I say. "Surely this is what move semantics are for." So, I change the signature to

void foo(boost::dynamic_bitset<>&& db);

But then, calling foo(boost::dynamic_bitset<>{5}.set()) gives a compiler error, cannot bind 'boost::dynamic_bitset<>' lvalue to 'boost::dynamic_bitset<>&&'. I must instead call foo(std::move(boost::dynamic_bitset<>{5}.set())) and then everything works.

Why do I need to call std::move? This seems like it's clearly an xvalue (a temporary about to expire), doesn't it?


Solution

  • From the boost docs:

    dynamic_bitset& set();
    

    set() returns an lvalue.

    In general, if a function returns by value, or by rvalue reference:

    dynamic_bitset   set();
    dynamic_bitset&& set();
    

    then the expression set() is an rvalue (in the first case a prvalue, and in the second an xvalue). However because in the actual code set() returns an lvalue reference, the expression set() is an lvalue.

    category values http://downloads.sehe.nl/stackoverflow/value-categories.svg