Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.144.116.159] |
|
Страницы: (3) 1 [2] 3 все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
Цитата Serpentus @ Это можно только гадать. Может просто не додумались. Или не посчитали нужным (или даже вредным, Qraizer объяснил почему). Всегда должны быть причины. Очень сильно сомневаюсь, что они там до чего-то не додумались. Цитата Serpentus @ . А еще у вызова через std::function нулевой оверхед, а у вызова через std::unique_ptr<CFunction> - нет (пусть и не большой). Подозреваю, что std::function реализована точно также - виртуальный метод, динамическое создание класса и т.д. Т.е. оверхед абсолютно такой же. |
Сообщ.
#17
,
|
|
|
В общем, остановился вот на таком варианте
template <typename> struct CFunctionInvoker; template <size_t... Indexes> struct CFunctionInvoker<std::index_sequence<Indexes...>> { template <typename TFunc, typename TArgs, typename... TT> static auto Invoke(TFunc &&func, TArgs &&args, TT&&... args2) { return std::invoke(func, std::get<Indexes>(args)..., std::forward<TT>(args2)...); } template <typename TFunc, typename TArgs, typename... TT> static auto InvokeOnce(TFunc func, TArgs args, TT&&... args2) { return std::invoke(func, std::move(std::get<Indexes>(args))..., std::forward<TT>(args2)...); } template <typename TFunc, typename... TT> static auto Invoke(TFunc &&func, std::tuple<>, TT&&... args2) { return std::invoke(func, std::forward<TT>(args2)...); } template <typename TFunc, typename TArgs, typename... TT> static auto InvokeOnce(TFunc func, std::tuple<>, TT&&... args2) { return std::invoke(func, std::forward<TT>(args2)...); } }; template <typename TRes, typename... TArgs> class CFunction { public: typedef std::unique_ptr<CFunction> TPtr; template <typename TFunc, typename... TT> static TPtr Make(TFunc &&func, TT&&... args) { return std::make_unique<CFunctionImpl<CFunction, TFunc, TT...>>(std::forward<TFunc>(func), std::forward<TT>(args)...); } virtual ~CFunction() {;} template <typename... TT> TRes Call(TT&&... args) { return _Call(std::forward<TT>(args)...); } template <typename... TT> TRes operator()(TT&&... args) { return Call(std::forward<TT>(args)...); } protected: virtual TRes _Call(TArgs... args) = 0; template <typename TFunction, typename TFunc, typename... TT> class CFunctionImpl : public TFunction { public: CFunctionImpl(TFunc &&fn, TT&&... args) : m_fn(std::forward<TFunc>(fn)) , m_args(std::forward<TT>(args)...) { } virtual TRes _Call(TArgs... args) override { return CFunctionInvoker<std::index_sequence_for<TT...>>::Invoke(m_fn, m_args, args...); } protected: TFunc m_fn; std::tuple<std::decay_t<TT>...> m_args; }; }; Избавился от std::placeholders. На большее меня не хватает. |
Сообщ.
#18
,
|
|
|
Цитата Serpentus @ Я уверен, что правильно указал причину. Во всех официальных вариантах реализаций, что в крайне скудном функционале C++98, что в более мощном варианте, появившемся в бусте, функциональные объекты полностью отвечали семантике значений. Их можно было свободно и копировать, и присваивать, в общем делать всё то, что обычный программист привык делать с обычными переменными. Если б в C++11 решили, что это не нужно, программисты б сильно удивились. ...Это можно только гадать. Может просто не додумались. Или не посчитали нужным (или даже вредным, Qraizer объяснил почему). |
Сообщ.
#19
,
|
|
|
Цитата Qraizer @ Их можно было свободно и копировать, и присваивать, в общем делать всё то, что обычный программист привык делать с обычными переменными. Там не в копировании проблема, а отсутствии move |
Сообщ.
#20
,
|
|
|
Так я как раз об этом. Копирование и перемещение – это разные семантики. Я бы не хотел вдруг узнать что после a=b источник разрушен. Конечно, по дефолту так и не будет, ибо нужно хотя бы a=std::move(b) написать. Но дело не в этом. Из-за документированной семантики копирования куча библиотек уже использовали функциональные объекты в части своей реализации. И как именно, одним только их разработчикам ведомо, и то не факт. Если теперь весь использующий их софт вдруг начнёт использовать std::function с поддержкой объектов, использующих только семантику перемещения, один чёрт знает, как это может аукнуться даже на самих таких библиотеках, не говоря уже об этом софте.
Добавлено В общем, если это свой собственный велосипед, это ещё куда ни шло, хотя бы видно, что не std. Но в std такую фичу вводить вряд ли будут, опасно это. |
Сообщ.
#21
,
|
|
|
Цитата Qraizer @ Так я как раз об этом. Копирование и перемещение – это разные семантики. Никто с этим не спорит. Проблема в том, что там ты ничего не копируешь. Где ты видишь копирование в std::function<....> fn = std::bind(.....)? Т.е. std::function обязательно требует наличие конструктора копирования для rvalue. Зачем? Цитата Qraizer @ Если теперь весь использующий их софт вдруг начнёт использовать std::function с поддержкой объектов, использующих только семантику перемещения, один чёрт знает, как это может аукнуться даже на самих таких библиотеках, не говоря уже об этом софте. Что значит "только семантику перемещения"? Контейнеры только её используют? Чем std::function отличается от них? |
Сообщ.
#22
,
|
|
|
Цитата Олег М @ Подозреваю, что std::function реализована точно также - виртуальный метод, динамическое создание класса и т.д. Т.е. оверхед абсолютно такой же. Зачем подозревать? Просто откомпилируй и в отладчике на месте вызовов функций посмотри в Disassembly: в вызове std::function поверх обычной функции ровно те же инструкции, что и при вызове самой функции, в твоем коде инструкций больше. Оверхед небольшой и в 99+% случаев на него можно забить, но в STL стараются все оптимизировать по максимуму. |
Сообщ.
#23
,
|
|
|
Цитата Serpentus @ Зачем подозревать? Просто откомпилируй и в отладчике на месте вызовов функций посмотри в Disassembly: в вызове std::function поверх обычной функции ровно те же инструкции, что и при вызове самой функции, в твоем коде инструкций больше. Оверхед небольшой и в 99+% случаев на него можно забить, но в STL стараются все оптимизировать по максимуму. А ты std::function инициализировал через std::bind с параметрами? |
Сообщ.
#24
,
|
|
|
Цитата Олег М @ Проехали.Т.е. std::function обязательно требует наличие конструктора копирования для rvalue. Зачем? Цитата Олег М @ Контейнеры крайне редко использовались программистами в роли обычных переменных обычным для программиста способом, т.к. все понимали, что их копирование суть тяжёлая операция и всячески старались это оптимизировать, а уж для библиотечного кода и подавно. Что значит "только семантику перемещения"? Контейнеры только её используют? Чем std::function отличается от них? |
Сообщ.
#25
,
|
|
|
Цитата Qraizer @ Проехали. И что же мы проехали? Насколько мне известно, std::function появилась в 11-м стандарте, где move-семантика уже вовсю использовалась. О каком старом коде и о каких привычках программистов может идти речь? Объясни мне, почему вот этот код компилируется std::unique_ptr<std::string> sp(new std::string("!!!!!!!!!!!!!!!!!")); auto fn = [sp = std::move(sp)]() { std::cout << *sp << std::endl; }; auto fn2 = std::move(fn); fn2(); А вот этот - нет std::unique_ptr<std::string> sp(new std::string("!!!!!!!!!!!!!!!!!")); std::function<void()> fn = [sp = std::move(sp)]() { std::cout << *sp << std::endl; }; Хотя принципиального отличия лично я не вижу. Цитата Qraizer @ Контейнеры крайне редко использовались программистами в роли обычных переменных обычным для программиста способом, т.к. все понимали, что их копирование суть тяжёлая операция и всячески старались это оптимизировать, а уж для библиотечного кода и подавно. Т.е. копирование обычной переменной это считается нормальным? Или я как-то не понимаю, что значит "обычная переменная"? |
Сообщ.
#26
,
|
|
|
Цитата Олег М @ Объясни мне, почему вот этот код компилируется А вот этот - нет Хотя принципиального отличия лично я не вижу. Потому что в первом случае - некий сгенерированный компилятором класс, который (в данном случае) будет Moveable, но не CopyConstructable (согласно требованиям стандарта), а во втором - std::function, со строго специфицированным внешним поведением. Ну не может у тебя std::function быть в одном случае копируемым, а в другом - перемещаемым (в зависимости от того, чем его инициализировали). А вот лямбда-класс - может, потому что безымянный с минимально специфицированным публичным интерфейсом. |
Сообщ.
#27
,
|
|
|
Да уж. Дошло наконец-то.
Т.е. действительно, здесь без виртуального метода не обойтись. |
Сообщ.
#28
,
|
|
|
Фуф.
|
Сообщ.
#29
,
|
|
|
Цитата Олег М @ А ты std::function инициализировал через std::bind с параметрами? И так, и сяк. Двоичный код при вызове std::function идентичен коду при вызове того, чем он проинициализирован. Это ожидаемо, потому что std::function копирует внутрь себя объект, которым инициализируется, после что вызывает эту копию. (Под вызовом я имею в виду вызов "operator ()"). |
Сообщ.
#30
,
|
|
|
Цитата Serpentus @ И так, и сяк. Двоичный код при вызове std::function идентичен коду при вызове того, чем он проинициализирован. Это ожидаемо, потому что std::function копирует внутрь себя объект, которым инициализируется, после что вызывает эту копию. (Под вызовом я имею в виду вызов "operator ()"). Я думаю, если инициализация std::function и использование будут в разных единицах трансляции - то код будет немного другим, с косвенным вызовом. Но я могу ошибаться. |