Переместить семантику с помощью std::function
std::function
предоставляет конструктор из значения r. Что происходит с перемещенным функциональным объектом по стандарту? Будет ли он пустым, чтобы повторный вызов не имел никакого эффекта?
4 ответа
Под 20.8.11.2.1p6, function(function &&f)
листья f
в допустимом состоянии с неопределенным значением.
Пустое состояние является допустимым, поэтому следует ожидать, что перемещенный из функции объект может быть пустым.
Так как function
выполняет стирание типа, и функциональные объекты могут быть сколь угодно дорогими, поэтому имеет смысл оптимизировать оставление удаленного объекта пустым:
std::function<void()> g{std::bind{f, std::array<int, 1000>{}}};
std::function<void()> h{std::move{g}};
После h
был построен движением от g
можно было бы ожидать, что содержимое bind
были переведены из g
в h
а не копирование, так g
будет оставлено пустым.
Для следующей программы gcc 4.5.1 печатает empty
:
#include <functional>
#include <iostream>
void f() {}
int main() {
std::function<void()> g{f}, h{std::move(g)};
std::cout << (g ? "not empty\n" : "empty\n");
}
Это не обязательно самое оптимальное поведение; встраивание небольших вызываемых элементов (например, указателей на функции) создает ситуацию, в которой копирование вызываемого элемента более эффективно, чем его перемещение и очистка перемещенного объекта, поэтому другая реализация может оставить g
в непустом вызываемом состоянии.
Вокруг этого вопроса слишком много путаницы. Я собираюсь попытаться ясно изложить вещи...
В этом разделе описывается состояние перемещения объектов, определенных в std:
17.6.5.15 [lib.types.movedfrom]
Объекты типов, определенных в стандартной библиотеке C++, могут быть перемещены из (12.8). Операции перемещения могут быть явно указаны или неявно сгенерированы. Если не указано иное, такие перемещенные объекты должны быть помещены в допустимое, но неопределенное состояние.
Что это значит? Это означает, что с заданным в std перемещенным объектом можно делать с этим объектом все, что не требует априорного знания состояния этого объекта. К классу действий, которые не требуют априорных знаний о текущем состоянии, относятся те, которые не имеют предварительных условий.
Например, вы можете позвонить clear()
на переезде vector
потому что нет никаких предварительных условий на vector::clear()
, Но ты не можешь позвонить pop_back()
потому что у этого есть предпосылки.
Смотря конкретно на оператора звонка function
:
20.8.11.2.4 [func.wrap.func.inv]
R operator()(ArgTypes... args) const
Эффекты: INVOKE(f, std::forward(args)..., R) (20.8.2), где f - целевой объект (20.8.1) *this.
Возвращает: Ничего, если R пусто, в противном случае возвращается значение INVOKE (f, std::forward( args)..., R).
Броски: bad_function_call if!* This; в противном случае - любое исключение, создаваемое завернутым вызываемым объектом.
Обратите внимание, что нет предусловия или условия "Требуется". Это означает, что вызов оператора вызова function
отошли от function
не неопределенное поведение. Неважно, в каком состоянии function
в этом, вы не собираетесь нарушать какие-либо предварительные условия с этим вызовом.
Обратите внимание, что ни в коем случае в спецификации не говорится, что вызов не будет иметь никакого эффекта. Так что не иметь никакого эффекта не возможно.
Вызов либо вызовет упакованную функцию, либо выдает bad_function_call
, Это единственные два варианта. И какое поведение он имеет, зависит от состояния function
объект. И состояние function
объект не указан ([lib.types.movedfrom]).
Что происходит с перемещенным функциональным объектом по стандарту?
Он будет в допустимом состоянии (таким образом, объект может использоваться), но фактическое состояние, в котором он находится, не определено. Последняя часть означает, что вызов любой функции, которая требует, чтобы объект находился в определенном состоянии, не обязательно будет работать.
Будет ли он пустым, чтобы повторный вызов не имел никакого эффекта?
Вы не можете предполагать, что это будет. Вызов функции требует, чтобы у нее была функция для вызова. Это часть его состояния. А так как состояние не указано, результаты его вызова не определены.
Если вы хотите снова использовать объект осмысленно, просто создайте новый function
и назначьте это ему:
function<...> old;
function<...> new_ = std::move(old);
old = function<...>(...); //Reset to known state.
old(...); //Call is well-defined.
[Func.wrap.func.con]:
function(function&& f);
template <class A> function(allocator_arg_t, const A& a, function&& f);
Эффекты: если! F, * это не имеет цели; в противном случае, перемещение-создает цель f в цель *this, оставляя f в допустимом состоянии с неопределенным значением.