Использование `std:: function <void (...)>` для вызова функции non-void
Некоторое время назад я использовал std::function
примерно так:
std::function<void(int)> func = [](int i) -> int { return i; };
В основном, я сделал это, потому что я хотел хранить различные объекты функции в std::function
, но я не хотел ограничивать возвращаемые типы этих функций. Поскольку это, казалось, сработало, я пошел с ним. Но я не уверен, что он безопасен в использовании, и я не смог найти на нем никакой документации. Кто-нибудь знает, является ли это использование законным? Или, в общем, какие правила для объекта, который можно безопасно назначить std::function
?
Изменить
Для пояснения проблема, о которой я беспокоюсь, заключается в том, что функция лямбда возвращает int
, а func
объявляется с типом возврата void
. Я не уверен, что все в порядке, особенно после вызова func()
.
Ответы
Ответ 1
В вашем коде есть поведение undefined. Он может работать или не работать так, как вы ожидаете. Причина, по которой он имеет поведение undefined, из-за 20.8.11.2.1 [func.wrap.func.con]/p7:
Требуется: F
должно быть CopyConstructible
. F
должен быть Callable (20.8.11.2) для типов аргументов ArgTypes
и возвращаемого типа R
.
Для F
, подлежащего вызову для типа возврата R
, F
должен вернуть что-то неявно конвертируемое в возвращаемый тип std::function
(void
в вашем случае). И int
неявно конвертируется в void
.
Я бы ожидал, что ваш код будет работать на большинстве реализаций. Однако по крайней мере одна реализация (libС++), она не скомпилируется:
test.cpp:7:30: error: no viable conversion from 'int (int)' to 'std::function<void (int)>'
std::function<void(int)> ff = f;
^ ~
По иронии судьбы, обоснование этого поведения связано с другим вопросом SO.
Другой вопрос представляет проблему с использованием std::function
. Решение этой проблемы, связанное с реализацией, принудительно выполняет условие Requires: во время компиляции. Напротив, решение этой проблемы проблемы запрещает реализацию из принудительного применения предложения Requires:.
Ответ 2
Ваш прецедент хорошо определен в соответствии со стандартом.
Вы создаете std::function
из вызываемого объекта [1]
§20.8.11.2.1/7:
template<class F> function(F f);
Требуется: F будет CopyConstructible. f должен быть вызванным (20.8.11.2) для типов аргументов ArgTypes и тип возврата R.
Так вы можете назвать f?
§20.8.11.2/2 гласит:
Вызываемый объект f типа F является вызываемым для типов аргументов ArgTypes и тип возврата R, если выражение INVOKE (f, declval<ArgTypes>()..., R)
, рассматриваемое как неоцененный операнд (П. 5), хорошо сформирована (20.8.2).
И определение INVOKE
гласит:
§20.8.2
-
Определите INVOKE (f, t1, t2, ..., tN)
следующим образом:... материал, связанный с указателями-членами/var указателями... - f(t1, t2, ..., tN)
во всех других случаях.
-
Определите INVOKE (f, t1, t2, ..., tN, R) as INVOKE (f, t1, t2, ..., tN)
, неявно преобразованный в R
.
И так как любой тип может быть неявно преобразован в void
, ваш код должен быть в порядке со стандартным компилятором. Как указано ниже, ниже подразумевается неявное преобразование в void, поэтому это не определено.
[1]: Я думаю, что лямбда считается как вызываемый объект здесь, хотя у меня нет ссылки на это. Ваша лямбда также может использоваться как указатель функции, поскольку она не отображает контекст
Ответ 3
Похоже, что это может быть нормально для анонимных функций.
Цитата из http://www.alorelang.org/release/0.5/doc/std_function.html (это не из стандартной библиотеки С++, однако похоже, что они используют что-то похожее там, где привязки на С++)
Объекты функций могут создаваться только с помощью определения функции, выражения анонимной функции или путем доступа к методу bound, использующего оператор dot (.).
Другим способом, который это может быть сделано, является сохранение указателя функции в auto, как показано здесь: http://en.wikipedia.org/wiki/Anonymous_function (раздел С++)