Подтвердить что ты не робот

Использование `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().

4b9b3361

Ответ 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 (раздел С++)