请考虑以下内容:
#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");
}
无法编译,因为它要复制file
,这是不可能的。
return std::make_tuple(std::move(file), data);
有效,但是我问自己»这是否意味着它正在复制而不是现在移动data
?«,所以我宁愿有一个通用的解决方案。
但都不是(希望至少在这里可以使用移动语义/复制省略):
const auto check_and_read = [](const auto & filename)
-> std::tuple<std::ifstream, std::vector<std::byte>> {
…
return {file, data}
也(不是应该从右值引用的元组中进行移动构造吗?)
const auto check_and_read = [](const auto & filename)
-> std::tuple<std::ifstream, std::vector<std::byte>> {
…
return std::forward_as_tuple(file, data);
似乎可以正常工作。
是否有某种标准的方法可以确保在返回多个参数时无需单独std::move
就可以进行移动构造?
答案 0 :(得分:8)
请考虑以下内容:
std::tuple<std::string, std::string> foo() {
std::string a = "hello";
return {a, a};
}
您在特定表达式中使用file
和data
是隐式安全地移动的事实,即使对于非常相似的表达式,也不意味着总是如此。
编译器必须确定已命名的标识符注定要被视为r值,这样的分析很快就会变得不合理地复杂。
答案 1 :(得分:7)
没有标准功能,但是应该可以使用(C ++ 17):
template <class... Types>
auto move_to_tuple(Types&&... args) {
return std::make_tuple(std::move(args)...);
}
答案 2 :(得分:2)
绝不会隐式执行移动,除非通常考虑省略但碰巧不可能的情况下作为回退。目前(至少据我所知),这意味着隐式移动仅在RVO和NRVO的回退时发生。
决不会考虑file
和data
的省略。首先,RVO和NRVO都不适用于它们。只考虑返回的元组,因为在这种情况下是RVO。因此:
return std::make_tuple(std::move(file), data);
将通过RVO消除元组,移动文件并复制数据。所以你应该这样做:
return std::make_tuple(std::move(file), std::move(data));