Передача аргументов функции только для перемещения в boost::thread constructor

#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/q3efKb

2. @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();
}
 

Живая демонстрация 2

Ответ №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 перемещается вместо копий.