Вопрос по c++, c++11, stl – Каково обоснование того, что std :: bind и std :: thread всегда копируют аргументы?

10

Хорошо известно, что стандартное поведение std :: bind и std :: thread заключается в том, что он будет копировать (или перемещать) передаваемые ему аргументы, и для использования семантики ссылок нам потребуется использовать обертки ссылок.

Does anyone know why this makes a good default behaviour? Esp. in C++11 with rvalue reference and perfect forwarding, it seems to me that it makes more sense to just perfectly forward the arguments.

std::make_shared though doesn't always copy/move but just perfectly forward the provided arguments. Why are there two seemingly different behaviour of forwarding arguments here? (std::thread and std::bind that always copy/move vs std::make_shared that don't)

В какой-то момент мой ответ содержал «и этот локальный выходит за рамки», но, очевидно, он был удален после редактирования: S mfontanini
Если вы свяжете ссылку с местным жителем, у вас будут проблемы. mfontanini
Я не буду, если вызываемая функция не будет выходить за рамки локальной. В C ++ есть много вещей, которые позволяют вам оторваться. У меня вопрос скорее к тому, что такое поведение кажется менее интуитивным. ryaner

Ваш Ответ

5   ответов
2

что C ++ по умолчанию использует семантику значений практически везде. А использование ссылок может легко создать проблемы, связанные с временем жизни упомянутого объекта.

Имеет смысл. Спасибо! ryaner
C ++ имеет много принципов :-). Одним из наиболее фундаментальных является то, что он использует семантику значений по умолчанию; Вы должны явно запросить ссылку или указатель, чтобы сделать это иначе. (Конечно, стандартная библиотека не всегда следует этому правилу - итераторы и предикаты всегда имеют значение, но другие вещи, как правило, различаются.)
Но разве это не принцип C ++, согласно которому программист знает, чего он хочет лучше всего? В случае свисающей ссылки программист должен быть осторожен, чтобы параметры его функции были по значению, если он этого хочет, и когда параметр нашей функции принимает ссылку, я ожидал, что вещи будут перенаправлены идеально в качестве ссылки для любой риск. Просто мысль. ryaner
Нет. Принцип заключается в том, что программист не платит по производительности за функции, которые он не использует. & Quot; знать, что ты делаешь & quot; вещь - это всего лишь средство для достижения этой цели, а не самоцель.
1

std::bind, таким образом, имеет смысл собирать все аргументы по умолчанию.

Общий вариант использования должен быть идентичен передаче указателя на функцию, не зная, где это может закончиться.

Лямбда дает программисту большую гибкость при принятии решения, будет ли лямбда жить за пределами области действия, из которой получены аргументы.

Первое предложение дает веские основания. Проголосовал. Благодарю. ryaner
Это тот же самый случай для std :: thread? В случае функции запуска потока менее вероятно, что она будет записана как лямбда. И принуждение к использованию лямбда-замыкания для передачи по ссылке может показаться странным? Кажется, что лямбда не решает все проблемы, вызванные этим здесь ... Я думаю? ryaner
1

которая создает функтор отложенного вызова (несколькоstd::bind-подобно, но без вложенных выражений / функций-заполнителей). Моей главной мотивацией был случай, который я нашел нелогичным:

using pointer_type = std::unique_ptr<int>;
pointer_type source();
void sink(pointer_type p);

pointer_type p = source();

// Either not valid now or later when calling bound()
// auto bound = std::bind(sink, std::move(p));
auto bound = std::bind(
    [](pointer_type& p) { sink(std::move(p)); }
    , std::move(p) );
bound();

Причина этого адаптера (который перемещает свой аргумент lvalue ref вsink) является то, что вызов оболочки возвращаетсяstd::bind всегда передает связанные аргументы как lvalues. Это не было проблемой, например, с.boost::bind в C ++ 03, поскольку это lvalue будет связываться либо с ссылочным аргументом базового объекта Callable, либо с аргументом значения через копию. Не работает здесь сpointer_type только для перемещения.

Я понял, что на самом деле нужно учитывать две вещи: как должны быть связаны аргументыstoredи как они должны бытьrestored (т.е. передается объекту Callable). Контроль, которыйstd::bind предоставляет вам следующее: аргументы хранятся в мелкой (с помощьюstd::ref) или обычным способом (используяstd::dec,ay с идеальным вперёд); они всегда восстанавливаются как lvalue (с cv-квалификаторами, унаследованными от собственной оболочки вызова). За исключением того, что вы можете обойти последнее с помощью небольшого лямбда-выражения адаптера на месте, как я только что сделал.

Это, возможно, много контроля и много выражения для сравнительно мало, чтобы учиться. В сравнении моя утилита имеет семантику вродеbind(f, p) (распад и сохранение копии, восстановление как lvalue),bind(f, ref(p)) (хранить мелко, восстановить как lvalue),bind(f, std::move(p)) (распасться и сохранить с ходу, восстановить как значение),bind(f, emplace(p)) (распасться и сохранить с ходу, восстановить как lvalue). Это похоже на изучение EDSL.

@authchirCode here (и тому подобноеemplace изhere). Вы правы в том, что правильно вызывать функтор только один раз (далее вызывается пустойpointer_type), и в дополнение к этому оболочка вызова доступна только для перемещения. Это все также относится к эквивалентнымstd::bind использование.
Такжеunit test показывает типичное использование.
Это очень интересная идея, не могли бы вы указать нам на реализацию, чтобы мы могли ее изучить? Также результирующий функтор может быть логически вызван только один раз? А что случилось, если мы скопировали получившийся функтор?
13

make_shared переходит к конструктору, который вызываетсяnow, Если конструктор использует семантику вызова по ссылке, он получит ссылку; если он вызовет по значению, он сделает копию. Нет проблем здесь в любом случае.

bind создает отложенный вызов функции, которая будет вызываться в некоторых неизвестных точках в будущем, когда локальный контекст потенциально исчезнет. Еслиbind используя идеальную переадресацию, вам придется скопировать аргументы, которые обычно отправляются по ссылке и неизвестно, что они действовали во время фактического вызова,store them somewhere, and manage that storage, С текущей семантикойbind делает это для вас.

7

std::bind а такжеstd::threadвызов функции по заданным аргументам откладывается с сайта вызова. В обоих случаях, когда именно будет вызвана функция, просто неизвестно.

Для пересылки параметров напрямую в таком случае потребуется сохранение ссылок. Что может означать хранение ссылок наstack объекты. Который может не существовать, когда вызов фактически выполняется.

К сожалению.

Лямбда может сделать это, потому что вам дана возможность решать, на основе каждого захвата, хотите ли вы захватить по ссылке или значению. С помощьюstd::refВы можете привязать параметр по ссылке.

Имеет смысл. Проголосовал! Принял бы, если бы мне было позволено принять два ответа. Благодарю. ryaner

Похожие вопросы