Reputation: 93
I'm trying to create a vector of objects of a custom class A:
class A {
std::ifstream _file;
std::string _fileName;
public:
A(std::string &fileName) : _fileName(fileName) {
this->_file = std::ifstream(this->_fileName, std::ios::in);
}
~A() {
this->_file.close();
}
};
In the main I'm pushing n objects of class A with a for loop iterating a vector of file names.
Example:
#include <iostream>
#include <string>
#include <vector>
#include "A.hpp"
int main() {
std::vector<A> AList;
std::vector<std::string> fileList = { "file1", "file2", "file3" };
for (auto &f : fileList) {
std::cout << f << std::endl;
A tempObj(f);
AList.emplace_back(tempObj);
}
return 0;
}
But I get this error:
/usr/include/c++/9.1.0/bits/stl_construct.h:75:7: error: use of deleted function ‘A::A(const A&)’
If I'm not wrong, since I have a member std::ifstream inside my class A, the copy constructor is deleted (Ref: https://en.cppreference.com/w/cpp/io/basic_ifstream/basic_ifstream)
How can I solve it? What I'm doing wrong?
Thanks for your help
Upvotes: 2
Views: 1930
Reputation: 798
If I'm not wrong, since I have a member std::ifstream inside my class A, the copy constructor is deleted (Ref: https://en.cppreference.com/w/cpp/io/basic_ifstream/basic_ifstream)
You are right, std::basic_ifstream
is an example of uncopyable type, because it's illogical to have several copies of a single stream. In this situation you should use move semantics. Just add move constructor to your class definition:
A(A&& other) noexcept
: _file(move(other._file))
, _fileName(move(other._fileName))
{}
And now your code works fine, because vector elements are properly constructed during pushing:
std::vector<A> AList;
for (auto &f : { "file1", "file2", "file3" })
AList.push_back(A(f));
Upvotes: 1
Reputation: 96336
As noted in the other answer, class A
is non-copyable because its member std::ifstream _file;
is non-copyable.
But std::ifstream
is movable, and normally your class would be movable as well, but providing a custom destructor prevents the implicit generation of a move constructor and a move assignment operator (see the chart here).
So, step 1: Make your class movable by removing the custom destructor (the implicitly generated destructor will do the same thing anyway).
Alternatively, if you want to keep the destructor for some reason, you need to ask your compiler to generate a move constructor and a move assignment operator:
A(A &&) = default;
A &operator=(A &&) = default;
Step 2: When adding an instance of class A
to the vector, move it instead of copying:
A tempObj(f);
AList.emplace_back(std::move(tempObj));
// ^^^^^^^^^^ ^
Alternatively, construct it directly in the vector:
AList.emplace_back(f);
Upvotes: 2
Reputation: 596672
Like you said, your A
class is not copyable due to the ifstream
member, which can't be copied. So your class's copy constructor is deleted by default.
But you are trying to copy-construct an A
object when you pass tempFile
to emplace_back()
.
You need to pass the filename instead to emplace_back()
and let it construct the A
object inside the vector for you by forwarding the string to your constructor:
std::vector<A> AList;
std::vector<std::string> fileList;
for (auto &f : fileList)
{
AList.emplace_back(f);
}
On a side note, your constructor can and should initialize the ifstream
in the member initialization list rather than in the constructor body:
A::A(std::string &fileName)
: _file(fileName), _fileName(fileName)
{
}
Upvotes: 5