I have a function that returns a std::vector<std::byte>
I am aware that std::byte
is not a character type nor an integral type, and that converting it to char is only possible through a typecast. So far so good.
So I would like (in cases where I know that the vector only contains character data) to transfer ownership of the underlying buffer from the std::vector<std::byte>
to a std::vector<char>
using std::move
, so as to avoid copying the entire underlying buffer.
When I try doing this, I get this error:
no suitable user-defined conversion from "std::vector<std::byte, std::allocatorstd::byte>" to "std::vector<char,std::allocator>" exists
Is this at all possible using C++? I think there are real use cases where one would want to do this
I would probably leave the data in the original vector<byte>
and make a small class that keeps a reference to the original vector<byte>
and does the necessary casting when you need it.
Example:
#include <cstddef>
#include <iostream>
#include <vector>
template<typename T>
struct char_view {
explicit char_view(std::vector<T>& bytes) : bv(bytes) {}
char_view(const char_view&) = default;
char_view(char_view&&) = delete;
char_view& operator=(const char_view&) = delete;
char_view& operator=(char_view&&) = delete;
// capacity
size_t element_count() const { return bv.size(); }
size_t size() const { return element_count() * sizeof(T); }
// direct access
auto data() const { return reinterpret_cast<const char*>(bv.data()); }
auto data() { return reinterpret_cast<char*>(bv.data()); }
// element access
char operator[](size_t idx) const { return data()[idx]; }
char& operator[](size_t idx) { return data()[idx]; }
// iterators - with possibility to iterate over individual T elements
using iterator = char*;
using const_iterator = const char*;
const_iterator cbegin(size_t elem = 0) const { return data() + elem * sizeof(T); }
const_iterator cend(size_t elem) const { return data() + (elem + 1) * sizeof(T); }
const_iterator cend() const { return data() + size(); }
const_iterator begin(size_t elem = 0) const { return cbegin(elem); }
const_iterator end(size_t elem) const { return cend(elem); }
const_iterator end() const { return cend(); }
iterator begin(size_t elem = 0) { return data() + elem * sizeof(T); }
iterator end(size_t elem) { return data() + (elem + 1) * sizeof(T); }
iterator end() { return data() + size(); }
private:
std::vector<T>& bv;
};
int main() {
using std::byte;
std::vector<byte> byte_vector{byte{'a'}, byte{'b'}, byte{'c'}};
char_view cv(byte_vector);
for(char& ch : cv) {
std::cout << ch << '\n';
}
}
Output:
a
b
c
A simpler option if you only need const
access could be to create a string_view
:
template<typename T>
std::string_view to_string_view(const std::vector<T>& v) {
return {reinterpret_cast<const char*>(v.data()), v.size() * sizeof(T)};
}
//...
auto strv = to_string_view(byte_vector);
std::vector
does not allow attaching or detaching to memory allocations , other than moves from a vector of exactly the same type. This has been proposed but people raised (valid) objections about the allocator for attaching and so on.
The function returning vector<byte>
constrains you to work with a vector<byte>
as your data container unless you want to copy the data out.
Of course, you can alias the bytes as char
in-place for doing character operations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With