这个问题看起来和这里的其他问题很相似,但是一些关键的细节在这个例子中是不同的(他们总是把迭代器的元素复制到向量中,就我所看到的帖子而言)。
坚持一个非常常见的习惯用法(据我所知),我使用file
-〉ifstream
-〉istream_iterator
-〉vector
(这种方法在向量的类型上调用>>
)。
取消引用仅返回最近读取的对象的副本
在我的例子中,我想读取一些包含向量成员的对象,这意味着当前读取的对象被复制以插入到向量中(这意味着复制构造整个向量成员),并且在阅读新对象时立即构造全新的对象。
所以这个副本是不需要的,只是拖慢了整个过程。有没有办法消除这个不需要的副本(我想,istream_iterator
可能只是move
把对象取出,然后构造一个新的对象,但也许有另一种方法可以避免这种复制)?
有关该问题的说明,请参见一些示例代码:
#include <fstream>
#include <iostream>
#include <iterator>
#include <vector>
struct My_class {
std::vector<std::string> vec;
// default/copy/move constructors to illustrate what constructors are being invoked
My_class() : vec{} {
std::cout << " default" << std::endl;
}
My_class(My_class const &other) : vec{other.vec} {
std::cout << " copy" << std::endl;
}
My_class(My_class &&other) noexcept : vec{std::move(other.vec)} {
std::cout << " move" << std::endl;
}
friend std::istream& operator>>(std::istream& is, my_class& i) {
i.vec.clear(); // needed since i still contains the data from the previous object read
std::string l;
// in other use cases some more lines would be read or maybe the lines get preprocessed (e.g. split it up) and then inserted into the vector
std::getline(is, l);
i.vec.push_back(l);
return is;
}
};
int main(int argc, char* argv[]) {
std::ifstream ifs{argv[1]};
std::vector<my_class> data{std::istream_iterator<my_class>{ifs}, {}};
for(const auto& _ele : data){
for(const auto& ele : _ele.vec){
std::cout << ele << " ";
}
std::cout << std::endl;
}
}
第二次尝试:
#include <fstream>
#include <iostream>
#include <iterator>
#include <vector>
#include <memory>
#include <ranges>
static int cpy_cnt = 0;
struct My_class {
std::vector<std::string> vec;
// default constructor
My_class() : vec{} {
std::cout << vec.size() << " default" << std::endl;
}
My_class(My_class&&) = default;
My_class(const My_class&) = default;
My_class& operator=(My_class&&) = default;
My_class& operator=(My_class&) = default;
friend std::istream& operator>>(std::istream& is, My_class& i) {
i.vec.clear();
std::string l;
std::getline(is, l);
i.vec.push_back(l);
return is;
}
};
int main(int argc, char* argv[]) {
std::ifstream ifs{argv[1]};
std::cout << "iter create" << std::endl;
auto tmp = std::views::istream<My_class>(ifs);
std::cout << "vec create" << std::endl;
std::vector<My_class> data2{};
}
给出包含error: no matching function for call to '__begin'
的错误(不会粘贴完整的长错误消息clang)。
我一直在摆弄gcc
和clang
,它的接缝,错误只发生在clang
,而与gcc
一切都很好。
使用cd build && cmake -DCMAKE_BUILD_TYPE=Debug -D CMAKE_C_COMPILER=clang -D CMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..
和cd build && cmake -DCMAKE_BUILD_TYPE=Debug -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..
进行配置
$ g++ --version
g++ (GCC) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ clang++ --version
clang version 14.0.6
Target: x86_64-pc-linux-gnu
Thread model: posix
1条答案
按热度按时间ttygqcqt1#
正如您所发现的,引用
istream_iterator
将为您提供一个副本。一个解决方法是使用views::istream
和ranges::move
:根据您的用例,您也可以直接在
ifs_view
上循环以避免任何构造(因为您的operator >>
直接将数据插入到底层向量中):旁注,
istream_view
要求对象是可赋值的,而您的My_class
的赋值操作符现在被隐式删除了,所以您需要自己至少提供一个,或者只是默认复制/移动构造函数。