#c #multithreading #c 11 #boost #boost-thread
#c #многопоточность #c 11 #boost #boost-поток
Вопрос:
Для std::thread
. Он выводит 10 в качестве выходных данных, чего я и хочу.
void foo(std::unique_ptr<int> amp;amp; in) {
std::cout << *in;
}
int main(){
auto in = std::make_unique<int>(10);
std::thread t(foo, std::move(in));
t.join();
}
Но аналогичная попытка с Boost 1.72 не удается скомпилировать
void foo(std::unique_ptr<int> amp;amp; in) {
std::cout << *in;
}
int main(){
auto in = std::make_unique<int>(10);
boost::thread t(foo, std::move(in));
t.join();
}
Error : note: copy constructor of 'list1<boost::_bi::value<std::unique_ptr<int>>>' is implicitly deleted because base class 'storage1<boost::_bi::value<std::unique_ptr<int>>>' has a deleted copy constructor template< class A1 > class list1: private storage1< A1 >
Я нахожу это удивительным, потому что в документации для boost::thread указано следующее :
Конструктор потоков с шаблоном аргументов <класс F, класс A1, класс A2,…> поток (F f, A1 a1,A2 a2, …);
Предварительные условия: F и каждый An должны быть копируемыми или перемещаемыми.
Поскольку я передаю a в качестве аргумента, я удовлетворяю критерию «подвижности». std::unique_ptr
Итак, мне интересно, почему boost thread создает std::unique_ptr
? Разве это не должно перемещать std::unique_ptr в объект потока, а затем перемещать его дальше в функцию потока, как это делает реализация для std::thread
?
Комментарии:
1. Хм…. Похоже, я не могу отредактировать свой пост… Я хотел написать «Интересно, почему boost thread COPY создает std::unique_ptr»
2. Можем ли мы получить полное сообщение об ошибке?
Ответ №1:
Согласно документации, boost::thread
аргументы используются точно так boost::bind
же, и эта комбинация функции и аргумента не подходит для boost::bind
(и для std::bind
, если на то пошло).
std::bind(foo, std::move(in))(); // error
boost::bind(foo, std::move(in))(); // error
std::thread
является более надежным, чем либо std::bind
или boost::bind
.
Если вам нужно переместить аргумент в foo
, вам нужно обернуть его в функцию или лямбда-выражение, которое принимает его по неконстантной ссылке lvalue и перемещает его в foo
. В противном случае просто измените foo
, чтобы принять неконстантный ссылочный аргумент lvalue.
Комментарии:
1. На самом деле
std::bind
работает, за исключением того, что он не передает значение rvalue вfoo
: godbolt.org/z/q3efKb2. @Yakk-AdamNevraumont Существует два разных значения
amp;amp;
.std::bind
работает с тем, который вы использовали, но не с другим.3. Это помощь, которая не передает значение rvalue. (Два разных значения
amp;amp;
— это оператор and и ссылка на значение rvalue; сворачивание ссылки заставляет ссылку на значение rvalue выводить ссылку на значение lvalue. Вы могли бы вызвать свертывание пакета ссылок и вычет типа аргумента шаблона с другим значениемamp;amp;
, но тогда было бы 3. 4, если вы разделите оператор короткого замыкания с оператором без короткого замыканияamp;amp;
. Несмотря на это, привязка std работает только с типами перемещения; и «большая надежность» потока std заключается в том, что вызываемый объект выполняется один раз ; та же логика в привязке std будет нарушена.
Ответ №2:
Похоже на ошибку в Boost.Документация по потоку. По умолчанию он не поддерживает аргументы только для перемещения, поскольку он передает их boost::bind
по значению вместо пересылки.
Но есть флаг BOOST_THREAD_PROVIDES_VARIADIC_THREAD
, который разрешает boost::thread
rvalue-конструкторы. Он устанавливается автоматически, когда другая плохо документированная переменная BOOST_THREAD_VERSION
> 4.
#define BOOST_THREAD_VERSION 5
#include <boost/thread.hpp>
. . .
Другим возможным обходным путем является использование лямбда.
void foo(std::unique_ptr<int> in) {
std::cout << *in;
}
int main() {
auto in = std::make_unique<int>(10);
boost::thread t([in = std::move(in)]() mutable { foo(std::move(in)); });
t.join();
}
Ответ №3:
Библиотека потоков boost использует boost bind, а boost bind предполагает, что вы можете вызывать вызываемый объект повторно, поэтому не выходит из него. Вы можете подделать это.
template<class T>
struct move_it_t {
T t;
operator T(){ return std::move(t); }
};
template<class T>
move_it_t<typename std::decay<T>::type> move_it(Tamp;amp; t)
return std::move(t);
};
затем
auto in = std::make_unique<int>(10);
boost::thread t(foo, move_it(in));
t.join();
что это делает, так это перенос in
в тип, который при приведении к T
перемещается вместо копий.