Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

return tuple of uncopyable objects

Tags:

c++

consider the following:

#include <fstream>
#include <tuple>
#include <utility>
#include <vector>

const auto length_of_file = [](auto & file){
    file.seekg(0, std::ios::end);
    std::streampos length = file.tellg();
    file.seekg(0, std::ios::beg);
    return length;
};

int main(int, char * []) {
    const auto check_and_read = [](const auto & filename){
        std::ifstream file(filename, std::ios::binary);
        file.exceptions(std::ios::failbit | std::ios::badbit);
        std::vector<std::byte> data(length_of_file(file));
        file.read(reinterpret_cast<char*>(data.data()), data.size());
        return std::make_tuple(file, data);
    };
    auto [file, data] = check_and_read("foo.txt");
}

This doesn’t compile because it wants to copy file which isn’t possible.

return std::make_tuple(std::move(file), data); works, but then i’m asking myself »Does this mean it’s copying instead of moving data now?«, so i would rather have a generic solution for this.

But neither (expecting move semantics/copy elision to kick in at least here):

const auto check_and_read = [](const auto & filename)
-> std::tuple<std::ifstream, std::vector<std::byte>> {

…

return {file, data}

nor (shouldn’t this move construct from a tuple of rvalue references?)

const auto check_and_read = [](const auto & filename)
-> std::tuple<std::ifstream, std::vector<std::byte>> {

…

return std::forward_as_tuple(file, data);

seems to work.

Is there some standard way to ensure move construction on return of multiple arguments without having to std::move each one individually?

like image 762
Darklighter Avatar asked May 12 '19 19:05

Darklighter


2 Answers

Consider the following:

std::tuple<std::string, std::string> foo() {
  std::string a = "hello";

  return {a, a};
}

The fact that your usage of file and data in your specific expressions is implicitely safely movable does not mean that it's always the case, even for very similar expressions.

The compiler has to be CERTAIN that the named identifier is doomed in order to treat it as a r-value, and such an analysis would quickly become unreasonably complex.

like image 197
Frank Avatar answered Sep 22 '22 01:09

Frank


There's no standard function, but this should work (C++17):

template <class... Types>
auto move_to_tuple(Types&&... args) {
    return std::make_tuple(std::move(args)...);
}
like image 22
jcai Avatar answered Sep 19 '22 01:09

jcai