1
\$\begingroup\$

I want to be able to construct and fill a vector of bytes using a stream (binary shift) operators. So I overloaded operator << (...).

Here is the code I want to write :

using ByteVector = std::vector<uint8_t>;

void send_message(MessageId id, std::vector<ByteVector> parameters);

void foo()
{
    std::string str = "A string object";
    int bar = 42;

    ByteVector baz = {0x42, 0x13, 0x37};

    send_message(MessageId::COMMAND, {
        ByteVector() << str,
        ByteVector() << bar,
        ByteVector() << baz
    });
}

I started by adding the following operator overloads :

ByteVector & operator << (ByteVector & bv, const std::string & str);
ByteVector & operator << (ByteVector & bv, uint8_t byte);
ByteVector & operator << (ByteVector & bv, const ByteVector & bv2);
ByteVector & operator << (ByteVector & bv, int value);

They were working well for following use case:

ByteVector bv;
std::string str = "Never gonna give you up";
bv << str;

But not the for previous example where my compiler (clang) shows the following error:

error: invalid operands to binary expression ('ByteVector' (aka 'vector<unsigned char>') and 'const std::string' (aka 'const basic_string<char, char_traits<char>, allocator<char> >'))

note: candidate function not viable: expects an l-value for 1st argument
ByteVector & operator << (ByteVector & bv, const std::string & str);
             ^
note: candidate function not viable: expects an l-value for 1st argument
ByteVector & operator << (ByteVector & bv, uint8_t byte);
             ^

note: candidate function not viable: expects an l-value for 1st argument
ByteVector & operator << (ByteVector & bv, const ByteVector & bv2);
             ^

note: candidate function not viable: expects an l-value for 1st argument
ByteVector & operator << (ByteVector & bv, int value);
             ^

I didn't really understand the error. I think the compiler want's to use the ByteVector() in ByteVector() << something as a rvalue (ByteVector &&).

So I added the following operator overload:

template<typename T>
ByteVector && operator << (ByteVector && bv, T data)
{
    return std::move(bv << data);
}

This overload solve the compiler error. But I would like to know if my implementation is good and optimal in terms of good practice, memory usage and efficiency.

\$\endgroup\$

closed as off-topic by Nikita B, Marc-Andre, Heslacher, alecxe, t3chb0t Aug 2 '17 at 13:34

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions containing broken code or asking for advice about code not yet written are off-topic, as the code is not ready for review. After the question has been edited to contain working code, we will consider reopening it." – Nikita B, Marc-Andre, Heslacher, alecxe, t3chb0t
If this question can be reworded to fit the rules in the help center, please edit the question.

1
\$\begingroup\$

Instead of returning an rvalue reference, it's better to return bv by value:

template<typename T>
ByteVector operator<< (ByteVector && bv, T data)
{
    return bv << data;
}

C++ compilers perform return value optimisation, which means that the return value won't be copied in this case.


An alternative approach is to ignore the advice not to subclass standard library containers, and implement your operators as member methods. That will remove the need to overload for rvalue types, and I believe it's safe to do so for this use.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.