Read file into vector I need to read a range of bytes from a file and return it as a std::vector&lt;std::byte&gt;.

My current code looks like this:

#include &lt;cstdint&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

using bytes = std::vector&lt;std::byte&gt;;
using namespace std;

bytes read_block(uint32_t offset,
                 uint32_t length,
                 const string&amp; filename) {
  ifstream is(filename, ios::binary);
  is.seekg(offset);
  byte data[length];
  is.read(reinterpret_cast&lt;char*&gt;(data), length);
  return bytes(data, data + length);
}


But as can be seen I read it into a byte array and then create the vector by copying the bytes in the constructor. I have some issues with this implementation:


Copying of bytes when creating output variable.
Use of C style array.
Use of casts.


I have left out error handling for now to focus on the specific issues above.
Nice:

using bytes = std::vector&lt;std::byte&gt;;


I would call it Bytes to make it clear it is a type rather than an object.



This is not a good idea:

using namespace std;


See: Why is “using namespace std;” considered bad practice?



This is technically not valid C++.

  byte data[length];


Variable sized arrays (VSA) are an extension to the language supported by several compilers but not actually part of the C++ standard. Though it is part of the more recent versions C standard.

Also creating a local array means that the data is being created on the local stack frame (here I assuming a Von-Neumann like architecture). This means for the data to exist after the function completes means you will need to copy it out of the function. So an alternative data object is probably a good idea.

Also there is a limitation on the size of array allowed on the stack (there are hardware limitations on the size of stack frames on lots of architecture).



So a god idea would be to use the same data structure that you return.

  Bytes data(length);   // This is basically std::vector


Now in older versions of C++ there are some nice optimizations that can be applied to std::vector when it is returned from a function that allow the compiler to build the std::vector at the final destination so that it is not actually copied on return.

In modern versions of the compiler the language has the concept of move semantics built into the language. So returning a std::vector is not an expensive operation as the internal buffer is moved (just the pointer is copied) from the function to the destination result.

Also the compiler is now required to perform copy elision of objects when it can.



Error checking

This code does zero error checking. There are several things that can go wrong and you don't check for any of them. But you said you removed this (you should have left it in).



So a better version:

#include &lt;vector&gt;
#include &lt;string&gt;
#include &lt;fstream&gt;
#include &lt;cstdint&gt;

using Bytes = std::vector&lt;std::byte&gt;;

Bytes read_block(std::uint32_t      offset,
                 std::uint32_t      length,
                 std::string const&amp; filename)
{
  // Not going to explicitly check these.
  // The use of gcount() below will compensate for a failure here.
  std::ifstream is(filename, std::ios::binary);
  is.seekg(offset);

  Bytes data(length);
  is.read(reinterpret_cast&lt;char*&gt;(data.data()), length);

  // We have to check that reading from the stream actually worked.
  // If any of the stream operation above failed then `gcount()`
  // will return zero indicating that zero data was read from the
  // stream.
  data.resize(is.gcount());

  // Simply return the vector to allow move semantics take over.
  return data;
}