Reputation: 1144
This emits some 100 line error. error: use of deleted function of run_me copy constructor and so on. So, what's the problem here?
#include<thread>
#include<iostream>
#include<vector>
#include<fstream>
#include<string>
#include<chrono>
using namespace std;
//creating thread to print line from file
class run_me {
ifstream fs;
public:
string s;
run_me(): fs("lliftOff.cpp",ifstream::in) {}
void operator()(){
cout<<"hi";
while(getline(fs,s) ) {
cout<<s<<endl;
this_thread::sleep_for (chrono::seconds(1));
}
}
~run_me() {
fs.close();
}
};
int main() {
thread t1{run_me()};
t1.join();
cout<<"shutting down now"<<endl;
}
Upvotes: 2
Views: 2328
Reputation: 63807
THE EXPLANATION
The Standard mandates that the object, and the parameters, passed to the relevant constructor of std::thread
must be MoveConstructible
.
The problem with the given code is that class run_me
has an implicitly deleted copy/move-constructor since it has a non-copyable member; std::ifstream fs
.
SOLUTION #1 - CHANGE THE CALL SITE
To prevent the compiler from trying to copy class run_me
all we have to do is wrapping the object inside a lambda and initialize our std::thread
with this said object:
std::thread t1 {
[] { run_me {} (); }
};
Note: the above creates a temporary run_me {}
and calls operator()
on it inside an anonymous lambda, the temporary run me {}
will be created when the lambdas function is actually called, not when the lambda is created. When t1
calls anonymous-lambda ()
the function scope will be entered, and run_me {}
will be initialized.
SOLUTION #2 - CHANGE class run me
Normally we would just add a move-constructor to class run_me
that would initialize fs
with a std::move
d instance of the original fs
, not really a big issue.
Sadly implementations such as libstdc++
hasn't implemented the functionality required to properly move streams, this means that we cannot move Standard library streams around, yet.
If you are working with libstdc++
, or any other implementation that lacks movable streams, we will have to resort to some sort of hack when writing our solution, and I recommend the below:
#include <iostream>
#include <fstream>
#include <string>
#include <memory>
class run_me {
std::unique_ptr<std::ifstream> fs;
public:
string s;
run_me()
: fs (new std::ifstream ("foo.cpp",ifstream::in))
{ }
run_me (run_me&& src)
: fs (std::move (src.fs))
{ }
void operator()() {
while(getline(*fs,s) ) {
cout<<s<<endl;
this_thread::sleep_for (chrono::seconds(1));
}
}
~run_me() {
if (fs) // if we haven't been moved-from
fs->close();
}
};
Upvotes: 4
Reputation: 8824
your ifstream
implementation lack support for move operation, still deleting copy operation.
You could use a lambda to delay the run_me creation inside the thread
std::thread t { [] { run_me{}(); } };
If you wants to keep the run_me creation before the thread start, you could use std::ref
run_me rm;
thread t1{ []( run_me & rm ) { rm(); }, std::ref(rm) };
Upvotes: 5