1

The compiler wants my lvalue to be a rvalue reference and I dont see why.

My questions are:

  1. Why is "dataLen" const, even though it was declared non const and the lambda is told to catch by reference as default?
  2. Why does the compiler try to convert to rvalue reference "unsigned __int64 &&", even though it was declared "unsigned long long" (no rvalue reference) for tupleByteVector_content?

I think it is because of the lambda capture, but please see this simplified workflow below:

void read_socket()
{
  std::vector<std::tuple<unsigned long long, std::vector<unsigned char>>> tupleByteVector_content;
  read_socket_readSome(tupleByteVector_content, [this, &tupleByteVector_content]() {
    //use tuple vector
  });
}

//catch the tuple vector by reference
void read_socket_readSome(std::vector<std::tuple<unsigned long long, const std::shared_ptr<Session>& session, std::vector<unsigned char>>> & tupleByteVector_content, std::function<void()> && continueReadFunction)
{
  //Read data length from a asio socket
  std::shared_ptr<asio::streambuf> len_buffer = std::make_shared<asio::streambuf>();
  asio::async_read(session->connection->socket->next_layer(), *len_buffer, asio::transfer_exactly(1), [&, 
  this, session, len_buffer, tupleByteVector_content, continueReadFunction](const error_code& ec, std::size_t bytes_transferred) {

    //the first value I want to save
    unsigned long long dataLen = BytesToLength(len_buffer);

    //Read data from a asio socket
    std::shared_ptr<asio::streambuf> data_buffer = std::make_shared<asio::streambuf>();
    asio::async_read(session->connection->socket->next_layer(), *data_buffer, asio::transfer_exactly(dataLen), [&, this, dataLen, data_buffer, tupleByteVector_content, session, continueReadFunction](const error_code& ec, std::size_t bytes_transferred) {

        //ERROR HERE: ----------->

        std::tuple<unsigned long long, std::vector<unsigned char>> t = 
          std::make_tuple<unsigned long long, std::vector<unsigned char>>(

          dataLen, // ERROR C2664, cant convert argument 1 from "const unsigned __int64" to "unsigned __int64 &&"

          { asio::buffers_begin(data_buffer->data()), asio::buffers_end(data_buffer->data()) });

        //ERROR HERE: <-----------
        
        tupleByteVector_content.push_back(t);

        continueReadFunction();

    });
  });
}

EDIT: I was able to compile this tuple:

std::tuple<unsigned long long, std::vector<unsigned char>> t = { dataLen, { asio::buffers_begin(data_buffer->data()), asio::buffers_end(data_buffer->data()) } };

But then the push_back to the vector gives the error: error C2663: [...] ::push_back": for 2 overloads there is no conversion for the this-pointer (free translation into english from myself)

Natulux
  • 187
  • 1
  • 1
  • 11

1 Answers1

4
  1. dataLen is treated as const because you capture it by value:

    [&, this, dataLen,
              ^^^
    

By default function call operator generated for closure is marked as const, so inside const method you can only read data. Modifications are not allowed, unless you add mutable to definition of lambda.

  1. When you use make_tuple you should rely on template argument deduction instead putting types in explicit way, as you did it. Short version of your issue:

     int i;
     std::tuple<int> t = std::make_tuple<int>(i);
    

i is named object, so it is lvalue. By make_tuple<int> you make make_tuple signature look like: make_tuple(int&&). This is the place where compiler complains, because i as lvalue cannot be bound to rvalue reference. With argument deduction, parameter of make_tuple is deduced to be: int&, and in this case i can be bound.

push_back on vector doesn't work, because again you captured vector by value. push_back modifies object, which is not allowed when calling on const object. You should capture it by reference.

rafix07
  • 20,001
  • 3
  • 20
  • 33
  • Thank you, with your help I can compile the code. But I don't understand point 2. i is named and lvalue, but in every other function I can provide a lvalue just like I did. eg this does compile: std::unique_ptr l = std::make_unique(dataLen); Where is the difference? – Natulux Jul 15 '20 at 07:20
  • What puzzles me: This is maybe the first time, that I have to be less explicit in writing C++ code to make it work. I would expect this in a scripted language, but not in C++. I would expect at least an explicit way to write this, something like: std::tuple t = std::make_tuple NOREF(i); – Natulux Jul 15 '20 at 07:36
  • 1
    `make_tuple` uses forwarding reference for its arguments, `make_unique` also, but by writing `make_unique` you only specify `T`, not `Args`, so `Args` are deduced by rules of forwarding reference, that is why your example works. When passing lvalue, `Args` is `Args&`, when passing rvalue `Args` will be `Args&&`. – rafix07 Jul 15 '20 at 07:38