Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > C/C++: Общие вопросы > Можно ли как-то без временных переменных?


Автор: JoeUser 22.07.20, 02:53
Всем привет!

Накидал небольшой примерчик, и по нему есть вопрос:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <iostream>
    #include <functional>
     
    // ──────────────────────────────────────────────────────────────────────────────
     
    using CallbackType = std::function<void(void*)>;
     
    // ──────────────────────────────────────────────────────────────────────────────
     
    class Karbofos {
      
        CallbackType *Begin = nullptr;
        CallbackType *End = nullptr;
      
      public:
      
        const std::string Name = "Карбофос";
        
        void SetCallback(CallbackType *B, CallbackType *E) {
          Begin = B;
          End = E;
        }
        
        void Run() {
          try {
            if (Begin) (*Begin)(this);
            std::cout << "- Не отдам, слон - мой!\n";
            if (End) (*End)(this);
          } catch(const std::bad_function_call& E) {
            std::cout << "Хреновый вызов: " << E.what() << '\n';
          } catch(...) {
            std::cout << "Случилось страшное!\n";
          }
        }
        
    };
     
    // ──────────────────────────────────────────────────────────────────────────────
     
    void Shef(void* i) {
      std::cout << "- Стой, живодер " << ((Karbofos*)(i))->Name << "!\n";
    }
     
    // ──────────────────────────────────────────────────────────────────────────────
     
    void Kollega(void* i) {
      std::cout << "- " << ((Karbofos*)(i))->Name << ", ты немец и контрабандист!\n";
    }
     
    // ──────────────────────────────────────────────────────────────────────────────
     
    int main() {
      Karbofos Object;  
      CallbackType S = Shef;
      CallbackType K = Kollega;
      Object.SetCallback(&S,&K);
      Object.Run();
      return 0;
    }

Как мне в SetCallback передать сразу указатели на функции, без использования временных переменных?

Добавлено
Блин, переработался я :lol: new CallbackType(), решает ... осталось как-то возможную утечку памяти подпилить хитрыми указателями.

Автор: JoeUser 22.07.20, 04:14
На SO залепили на умных указателях. Ну выход. Ну почему просто так нельзя как-то?

Автор: Wound 22.07.20, 06:47
Цитата JoeUser @
Как мне в SetCallback передать сразу указатели на функции, без использования временных переменных?

У тебя прям талант придумывать себе проблемы. Убери указатели вообще у CallbackType. Зачем ты их туда написал? С какой целью ты их туда написал? Сам CallbackType - не является разве указателем(ну вернее он является std::function, но сам шаблонный параметр то указатель на функцию)?
Ты у себя прилепил указатель на указатель на функцию и спрашиваешь такие вещи.

https://ideone.com/miVYwD
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        #include <iostream>
        #include <functional>
        
        // ──────────────────────────────────────────────────────────────────────────────
        
        using CallbackType = std::function<void(void*)>;
        
        // ──────────────────────────────────────────────────────────────────────────────
        
        class Karbofos {
        
            CallbackType Begin = nullptr;
            CallbackType End = nullptr;
        
          public:
        
            const std::string Name = "Карбофос";
        
            void SetCallback(CallbackType B, CallbackType E) {
              Begin = B;
              End = E;
            }
        
            void Run() {
              if (Begin) (Begin)(this);
              std::cout << "- Не отдам, слон - мой!\n";
              if (End) (End)(this);
            }
        
        };
        
        // ──────────────────────────────────────────────────────────────────────────────
        
        void Shef(void* i) {
          std::cout << "- Стой, живодер " << ((Karbofos*)(i))->Name << "!\n";
        }
        
        // ──────────────────────────────────────────────────────────────────────────────
        
        void Kollega(void* i) {
          std::cout << "- " << ((Karbofos*)(i))->Name << ", ты немец и контрабандист!\n";
        }
        
        // ──────────────────────────────────────────────────────────────────────────────
        
        int main() {
          Karbofos Object;  
          //CallbackType S = Shef;
          //CallbackType K = Kollega;
          Object.SetCallback(Shef,Kollega);
          Object.Run();
          return 0;
        }


Добавлено
Цитата JoeUser @
На SO залепили на умных указателях. Ну выход. Ну почему просто так нельзя как-то?

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

Добавлено
Цитата JoeUser @
Блин, переработался я :lol: new CallbackType(), решает ... осталось как-то возможную утечку памяти подпилить хитрыми указателями.

"...Таких извращений Иван не видал!..." © Красная Плесень.

Автор: JoeUser 22.07.20, 07:07
Киля, ну прости! :lol: Спасибо.

Добавлено
Кстати, есть еще совет вот такой:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void SetCallback(CallbackType B, CallbackType E) {
      Begin = std::move(B);
      End = std::move(E);
    }

На сколько тут std::move нужен?

Автор: Wound 22.07.20, 07:36
Цитата JoeUser @
На сколько тут std::move нужен?

Без понятия, тонкости реализации я уже забыл, это пусть лучше Qraizer просветлит.
Но как по мне - он там нахрен не нужен.
Во первых, чтоб применять move - Нужен rvalue, т.е. функция должна принимать что то типа:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        void SetCallback(CallbackType&& B, CallbackType&& E) {
          Begin = std::move(B);
          End = std::move(E);
        }

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

ИМХО - это лишнее.

Автор: JoeUser 22.07.20, 07:37
Лан, ждем Qraizer8-)

Автор: Wound 22.07.20, 07:50
Ну и к слову вопрос еще состоит в том - можно ли перемещать указатели? У меня стойкое ощущение что указатель - это lvalue, а никак не rvalue.
Майерса год-два назад читал, там про это все написано.

Добавлено
Как мне помнится
lvalue - это то, что живет на следующей строчке, после его объявления.
rvalue - это то, что не живет на следующей строчке, после его объявления.

Что то типа там:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int a = 5; //! a = lvalue, 5 - rvalue
    SomeClass b = a.getSomeClass(); //! b - lvalue, a.getSomeClass() - rvalue(если возвращается копия объекта, а не какой нибудь кусок памяти в куче).
    void SomeFunc(std::string str); //! str - rvalue, потому что живет не дольше SomeFunc
    void SomeFunc2(const std::string& str)//! str - lvalue, потому что живет дольше SomeFunc

Это я так по памяти воспроизвел. Значит и указатель на функцию это 100% lvalue, и std::move, просто скопирует побайтно объект, никаких семантик перемещения не будет.

Добавлено
Разве что у тебя там std::function сможет переместится, что то я его проглядел, но это уже надо его конструкторы смотреть - если есть перемещающий, возможно и можно.

Автор: JoeUser 22.07.20, 08:27
Wound, я бегло глянул в сети, не вдавался в подробности. Пишут, что std::move как-то связан со списками захвата, если в качестве функции передается лямбда.

И вааще, Киля, ты должен оценить сейчас всю глубину глубин :lol:

user posted image

Автор: Wound 22.07.20, 08:42
Цитата JoeUser @
Пишут, что std::move как-то связан со списками захвата, если в качестве функции передается лямбда.

Ну тот код который привел ты - у тебя там нет лямбд :-?

Автор: JoeUser 22.07.20, 08:51
Цитата Wound @
Ну тот код который привел ты - у тебя там нет лямбд

За-то я написал "если" :)

Автор: Wound 22.07.20, 08:57
Цитата JoeUser @
За-то я написал "если"

Ну сам std::move вообще не занимается ничем, кроме каста входящего значения в rvalue, так что вряд ли он там может быть с чем то связан. Вот например можно посмотреть псевдро-реализацию std::move в блоге Майерса, ну или почитать его книжку, там об этом написано.
http://scottmeyers.blogspot.com/2012/11/on...of-stdmove.html

Автор: Qraizer 22.07.20, 14:21
Начнём с того, что функциональные объекты придумали как раз для того, чтобы добавить к "фрагменту кода" семантику значений и тем самым сделать работу с ним подобной работе с данными: запомнил, передал, переприсвоил, "частичные запомнил" аргументы, ...использовал запомненное, в смысле, вызвал. Лепить к ним ещё и указатели чаще всего бессмысленно, не реже, чем через поинтеры работать с обычными переменными, что лежат не в dynamic storage.
Продолжим тем, что ежели функция принимает неконстанту иначе, нежели по значению, это означает контракт, при котором она будет его менять, т.е. через него возвращается результат её работы. Поэтому Стандарт запрещает передавать rvalue под такие параметры, ибо они, являясь временными объектами, по возврату из функции просто не дают возможности этими результатами воспользоваться, ибо они будут разрушены на ближайшей ; (ну, почти всегда на ближайшей), а значит программист практически наверняка облажался, написав такую конструкцию.
Цитата JoeUser @
На сколько тут std::move нужен?

Цитата Wound @
Но как по мне - он там нахрен не нужен.
Во первых, чтоб применять move - Нужен rvalue, ...
Не спорю, запутаться с семантикой ссылок недолго. Сам путался, пока не привык. Но это несложно, не сложнее, чем разобраться с ссылками после C, где их не было.
Вы путаете причину и следствие. Немного ликбеза. std::move() нужен, чтобы ...скажем так, преобразовать (не правильный глагол на самом деле) объект к rvalue ref, изначально таковым не являвшимся. Нужно это конечно же для того, чтобы компилятор в этой точке смог использовать семантику перемещения, если сможет, ибо в противном случае он предпочтёт другие методы. Связано это с тем, что (и снова не вполне правильная терминология) перемещения обладают низким приоритетом при наличии других возможностей. А это в свою очередь связано с тем, что семантика перемещения подразумевает разрушение своего источника, что естественно весьма опасно для невременных объектов, поэтому надёжнее предположить, что "программист практически наверняка облажался, написав такую конструкцию" ©. Используя std::move(), программист чётко даёт понять компилятору, что он не буратина, и знает, что делает. Другой причины использовать std::move() нет. В частности его бессмысленно применять к объектам, которые и так уже rvalue ref.
И тут есть одна ловушка: rvalue ref – это просто контракт, срабатывающий при инициализации, который разрешает биндить ссылки на временные объекты, что иначе запрещено контрактом обычных ссылок по причине, как выше было аргументировано. Но в остальном это самые что ни на есть обычные ссылки, которые, как известно являются псевдонимами своих подссыльних объектов. Разница между этими типами ссылок в том, что обычная lvalue ссылка является псевдонимом lvalue, а rvalue ref псевдонимом rvalue. Это приводит к тому, что после инициализации любое использование lvalue ref сводится к lvalue, что практически не привносит в дальнейшем особых сложностей в семантику использования получившегося в итоге объекта, тогда как использование rvalue ref сводится к rvalue, что в связи с вышеозначенным низким приоритетом бинда rvalue к ссылочной семантике, очень часто приводит к необходимости постоянно повторять компилятору, что мы не буратина.
Поэтому, Wound, ты прав в том смысле, что std::move не помешает, чтобы компилятор смог использовать семантику перемещения в присваиваниях Begin и End, но для этого источникам нет необходимости быть обязательно rvalue ref, так что параметры SetCallback() могут быть и обычными значениями. Другое дело, что не объявив их rvalue ref, сам вызов SetCallback() при связывании аргументов с его параметрами не заюзает перемещения, аргументы в параметры скопируются, так что да, имеет смысл идти до конца и их также объявить как rvalue ref. Однако это уже не так однозначно, так как в результате контракт допускает, что при вызове SetCallback() её аргументы могут быть также разрушены в точке инициализации её параметров, а не только эти параметры в присваиваниях Begin и End, тогда как если параметры SetCallback() суть простые значения, подобного с аргументами не произойдёт: параметры, будучи их копиями, могут быть разрушены после перемещения Begin и End, т.к. к rvalue ref будут сведены они, т.е. разрушатся только лишь копии аргументов.
Что же касается
Цитата Wound @
В третьих, даже если ты явно не напишешь std::move, а по логике он там применим, компилятор сам выполнит перемещение(соптимизирует и выполнит)
то как я чуть выше сказал, компилятор сам этого не сделает, для него использование rvalue ref порождает новые rvalue объекты, которые сводить обратно к rvalue ref нужно заново. Точнее, иногда он всё-таки будет способен на подобное, но в очень редких случаях, например, если источником уже является некая несводимая rvalue ref, например возвращаемое значение некой функции, которая возвращает результат по rvalue ref.
Цитата Wound @
Во вторых, даже если все будет так - никаких гарантий нет что будет реализовано перемещение.
Ну это классика. Семантика перемещения для стандартных типов не описывает, что происходит с источниками, однако подавляющее большинство реализаций будут просто их копировать, так что никакой разницы от копирования нет. Но это не точно, Стандарт ничего тут не гарантирует, просто реализациям так проще. Что касается перемещений не стандартных типов, то тут всё в руках программиста, написавшего (или забившего на это) код перемещения.

Добавлено
Вот маленький пример для иллюстрации сказанного
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int b;
     
    int&& f(int&& z)
    {
      b = std::move(z);             // без std::move() скопилится, но и не разрушит z
     
    //  return z;                   не скопилится, нужен каст из rvalue к rvalue ref
      return std::move(z);
    }
     
    void g(int&& z)
    {
      // первый (внутренний) вызов без std::move() не скопилится, нужен каст из rvalue к rvalue ref
      // второй (внешний) вызов std::move() не требует, т.к. это несводимый контекст использования ссылок
      f(f(std::move(z)));
    }

Автор: JoeUser 22.07.20, 16:00
Qraizer, так в коде:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void SetCallback(CallbackType B, CallbackType E) {
      Begin = std::move(B);
      End = std::move(E);
    }

Все-таки std::move нужен, иначе будут копирования?

Автор: Qraizer 22.07.20, 16:34
Угу. Даже если там был бы только перемещающий operator=(), компилятор сам rvalue ref из rvalue не сделает, остановится с ошибкой.

Добавлено
С другой стороны копирование std::function без захваченных неPOD дешёвое, так что много не потеряешь.

Автор: OpenGL 22.07.20, 18:21
Цитата Wound @
Во первых, чтоб применять move - Нужен rvalue, т.е. функция должна принимать что то типа:

Не обязательно.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void MyClass::set_something(std::vector<Something> value)  // Передача по значению
    {
        this->something = std::move(value); // Переместит, лишних копирований не будет
    }


Цитата Qraizer @
Поэтому Стандарт запрещает передавать rvalue под такие параметры, ибо они, являясь временными объектами, по возврату из функции просто не дают возможности этими результатами воспользоваться, ибо они будут разрушены на ближайшей ; (ну, почти всегда на ближайшей), а значит программист практически наверняка облажался, написав такую конструкцию.

По мне так это просто недосмотр языка. По-моему не появится никаких проблем, если разрешить передавать rvalue в параметры-неконстантные ссылки, которых бы не было в случае параметров-константных ссылок.

Автор: JoeUser 22.07.20, 18:25
Пусть будет тут вторая часть истории с хэпиэндом :)

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <utility>
    #include <iostream>
    #include <functional>
     
    //──────────────────────────────────────────────────────────────────────────────
    using CallbackType = std::function<void(void*, int, int)>;
    // ──────────────────────────────────────────────────────────────────────────────
     
    class Karbofos {
        CallbackType Call;
      public:
        const std::string Name = "Карбофос";
        void SetCallback(CallbackType C) {
          Call = std::move(C);
        }
        void Run() {
          if (Call)
            Call(this, 1, 20);
          std::cout << "- Не продам!\n";
        }
    };
    //──────────────────────────────────────────────────────────────────────────────
    class Shef {
      public:
        void Entreaty(void* c, int a, int b) {
          std::cout << "- " << ((Karbofos*)c)->Name
                    << ", продай слона за "
                    << a << "." << b << "р ?\n";
        }
    };
    //──────────────────────────────────────────────────────────────────────────────
    int main() {
      Karbofos K;
      Shef S;
      K.SetCallback(std::bind(
                      &Shef::Entreaty, &S,
                      std::placeholders::_1,
                      std::placeholders::_2,
                      std::placeholders::_3)
                   );
      K.Run();
      return 0;
    }

Автор: Qraizer 22.07.20, 18:53
Цитата OpenGL @
По-моему не появится никаких проблем, если разрешить передавать rvalue в параметры-неконстантные ссылки, которых бы не было в случае параметров-константных ссылок.
Когда-то в бородатых 90-ых так и было. Можно было по неконстантной ссылке, например в int&, передавать результаты выражений типа a+b. Компилеры писали варнинг, мол, будет заюзана временная переменная, но код генерить это им не мешало. В итоге в C++98 это отменили, ибо это отвращает программеров писать правильные контракты в прототипах функций. Мол, const означает не изменение функцией, его отсутствие означает выходной параметр. А так как временные объекты в качестве выходных параметров использовать невозможно, то вызывающая сторона явно что-то мутит и не знает, что творит.

Добавлено
Цитата JoeUser @
Пусть будет тут вторая часть истории с хэпиэндом
Ух ты. Кто-то ещё пользует бинды...

Автор: OpenGL 22.07.20, 19:13
Цитата Qraizer @
А так как временные объекты в качестве выходных параметров использовать невозможно, то вызывающая сторона явно что-то мутит и не знает, что творит.

Вот именно с этим я и не согласен. Ну не нужен мне выходной параметр, их 3, но мне в данном случае достаточно одного - так редко это бывает? Более того, МСный компилятор это разрешает, и я что-то не припомню у себя багов, которые бы возникали из-за такого поведения. Просто по мне так если уж запрещать, то и для констант тоже для единообразия, либо разрешать для всего. И что это способствует ненаписанию конрактов я тоже не очень понимаю. Как именно способствует? Ты о том, что вместо const int & они могли объявить параметр как int&? Ну так это программист плохой, пусть учится, да и проставить const где надо это, прямо скажем, не квантовая теория поля, и делается на автомате.

Автор: Qraizer 22.07.20, 19:22
Цитата OpenGL @
Ну не нужен мне выходной параметр, их 3, но мне в данном случае достаточно одного - так редко это бывает?
Это когда функция вызывается, чтобы сделать работу, а тебе не нужен результат? Редко. Чаще бывает, что функция делает много разных работ, а тебе нужны не все, но это тоже плохой дизайн, правда, уже не твой, а автора той функции. Ещё во времена около C++98, а уж в доC++03 так подавно, сложились критерии отказоустойчивого кода, и декомпозиция, сведённая к нескольким задачам в одной интерфейсной единице, в эти критерии никак не укладывается. Почему, думаешь, положить в std::stack элемент можно одним методом, а выдёргивать оттуда двумя? Сначала получи значение с вершины, и потом отдельно удали из std::stack.

Автор: OpenGL 22.07.20, 19:26
А ещё, полагаю, с этим тесно связано следующее

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    std::string get_string();
     
    const std::string &x = get_string(); // Ok, string is alive
    std::string &y = get_string(); // Compiler error

что тоже не добавляет логичности и последовательности системе типов. Имхо, это всё должно либо не компилиться, либо падать при попытке доступа к мёртвому объекту.

Автор: Qraizer 22.07.20, 19:34
Цитата OpenGL @
Ну так это программист плохой, пусть учится, да и проставить const где надо это, прямо скажем, не квантовая теория поля, и делается на автомате.
Вот именно. Но когда на неконстантные ссылки биндятся rvalue, непонятно, то ли автор дурак, что хреново прописал контракт, то ли у тебя лыжи не едут, и ты тупо что-то не понимаешь в используемом тобою API. А так язык внятно говорит, что альтернативы нет.
По подобной причине C-стайл тайпкастов не в моде. Вот ты можешь сказать, что тут хотел написать программер?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void f(const unsigned long *param)
    {
    /* ... */
      unsigned int *ptr = (unsigned int*)param;
    /* ... */
    }
Если что, у меня три варианта.

Автор: OpenGL 22.07.20, 19:37
Цитата Qraizer @
Это когда функция вызывается, чтобы сделать работу, а тебе не нужен результат? Редко.

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

Автор: Qraizer 22.07.20, 19:40
Цитата OpenGL @
А ещё, полагаю, с этим тесно связано следующее

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    std::string get_string();
     
    const std::string &x = get_string(); // Ok, string is alive
    std::string &y = get_string(); // Compiler error

что тоже не добавляет логичности и последовательности системе типов.

Это как раз продолжение этой же темы с временными объектами. Функция тебе вернула временный объект, который вскоре будет убит. Однако нередко нужно его использовать неоднократно, поэтому без продления его времени жизни пришлось бы копировать. (Нынче можно ещё переместить, впрочем, что в большинстве случаев решит проблему.) Но без const ты мог бы непреднамеренно, например отдав в другую функцию по неконстантной ссылке, его поменять. Тебе было бы приятно узнать, что между двумя использованиями он внезапно изменился?

Автор: OpenGL 22.07.20, 19:41
Цитата Qraizer @
По подобной причине C-стайл тайпкастов не в моде. Вот ты можешь сказать, что тут хотел написать программер?

Не знаю, я C style casts указателей избегаю не из-за const, а в первую очередь из-за того, что они в конце-концов сведутся к reinterpret_cast-у.

Автор: Qraizer 22.07.20, 19:41
Цитата OpenGL @
Например, пусть это будет функция, которая как-то перефигачивает входной вектор и попутно записывает его в файл. Меня может интересовать запись в файл, но перефигаченный вот тут в месте вызова мне вообще не нужен, я просто хочу его записать в файл так, как его записывает эта функция.
Функция выполняет две задачи. Что ты мне хочешь этим доказать? Что она хреново спроектирована? Это я тебе и так скажу.

Добавлено
Цитата OpenGL @
Не знаю, я C style casts указателей избегаю не из-за const, а в первую очередь из-за того, что они в конце-концов сведутся к reinterpret_cast-у.
А какая разница, что сделает компилятор? Мы знаем, что он сделает, но вопрос звучал "что хотел сделать программист".

Автор: OpenGL 22.07.20, 19:47
Цитата Qraizer @
Это как раз продолжение этой же темы с временными объектами. Функция тебе вернула временный объект, который вскоре будет убит. Однако нередко нужно его использовать неоднократно, поэтому без продления его времени жизни пришлось бы копировать.

Да, это интересная мысль. При наличии перемещения это особо бесполезно, но заре плюсов вполне могло быть аргументом. Обдумать надо :)

Автор: JoeUser 23.07.20, 03:16
Цитата Qraizer @
Ух ты. Кто-то ещё пользует бинды...

Так нужно:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    K.SetCallback([](void* c, int a, int b){((Shef*)c)->Entreaty(c,a,b);});


???

Работает так же, но кажется что тут что-то не так :-?


И так работает:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    K.SetCallback([&](void* c, int a, int b){S.Entreaty(c,a,b);});


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

Че делать-та??? :wacko:

Автор: OpenGL 23.07.20, 10:01
Цитата Qraizer @
Функция выполняет две задачи. Что ты мне хочешь этим доказать? Что она хреново спроектирована? Это я тебе и так скажу.

Она выполняет одну задачу.

Ну и да, аргумент про продление времени жизни объекта при его присваивании в константную ссылку мне видится несостоятельным. Ты это обосновывал вот так
Цитата Qraizer @
Функция тебе вернула временный объект, который вскоре будет убит. Однако нередко нужно его использовать неоднократно, поэтому без продления его времени жизни пришлось бы копировать.

но это объяснение константность не использует. Точно так же я могу сказать "однако нередко нужно его использовать неоднократно и как-то менять". Не поменялось по-моему ровным счётом ничего. Да, ты дальше пишешь "Но без const ты мог бы непреднамеренно, например отдав в другую функцию по неконстантной ссылке, его поменять. ", но это обоснование того, зачем тебе в этом месте нужна константа, а я говорю о причинах, зачем делать такое исключение для константы и только для неё.

Автор: Qraizer 23.07.20, 11:39
Цитата OpenGL @
Она выполняет одну задачу.
Почему ты противоречишь себе?
Цитата OpenGL @
Точно так же я могу сказать "однако нередко нужно его использовать неоднократно и как-то менять".
Не чаще, чем не интересоваться возвращённым в rvalue результатом. P.S. Иногда мне нужно делить или умножать вещественные на степени двойки.

Добавлено
P.P.S. Если тебе нужно что-то странное, скажи об этом явно. Сделай переменную, инициализируй своим выражением и передавай агрументом в ссылку. И не интересуйся её дальнейшей судьбой на здоровье. Это ровно то, что ты хочешь, чтобы компилятор сделал за тебя. Компилятору нетрудно сделать это за тебя. Ему также нетрудно сдвигать вещественные или возвращать тебе результат % нам ними. Ему много что нетрудно.
Вопрос заключается в том, окупится ли профит от такого дружелюбия дебагом в нередких случаях. Чем раньше ты узнаешь о потенциальных проблемах в коде, тем быстрее и дешевле ты с ними разберёшься. Очень хорошо, когда компилятор понимает твои намерения, а не просто генерит объектный по указке. С вопросом выше ты так и не разобрался?

Автор: OpenGL 23.07.20, 13:05
Цитата Qraizer @
Почему ты противоречишь себе?

Я не противоречу. Ну да, функция выполняет две задачи, но они вместе могут рассматриваться как одна. Ты видишь в этом какое-либо противоречие?
Возможно, мы просто о разных функциях говорим. Если ты о публичных методах класса/модуля, то да, я бы скорей всего не стал эту функцию выносить туда. Как деталь реализации такая функция более чем имеет право на существование.

Цитата Qraizer @
Не чаще, чем не интересоваться возвращённым в rvalue результатом.

И это достаточно часто случается, если у тебя основное назначение функции не обработка переданных аргументов, а сайд-эффекты. Допустим, есть функция update_something, которая обновляет в базе некоторые записи по какому-то условию, и возвращает список ключей тех записей, которые были обновлены. Меня запросто в каком-то месте может интересовать только факт того, что обновление завершилось (не было брошено исключение, например), а сам список итоговых записей нафиг не нужен.

Цитата Qraizer @
Сделай переменную, инициализируй своим выражением и передавай агрументом в ссылку.

Да это понятно. Неудобно, что на ровном месте компилятор требует бесполезных действий, причём в точно таком же случае, но без константы, он их почему-то не требует. И это несмотря на mutable :D

Цитата Qraizer @
Вопрос заключается в том, окупится ли профит от такого дружелюбия дебагом в нередких случаях.

Мне кажется, что ты переоцениваешь полезность этой фичи. Я, например, с этим сталкивался только когда пытался откомпилить валидный с т.з. майкрософта код на ином компиляторе. Багов же из-за этого я не ловил.

Цитата Qraizer @
С вопросом выше ты так и не разобрался?

Ты про какой вопрос? Если про то, что хотел сделать программист c-style cast-ом, то он с обсуждением не связан.

Автор: Qraizer 23.07.20, 14:18
Цитата OpenGL @
Ну да, функция выполняет две задачи, но они вместе могут рассматриваться как одна. Ты видишь в этом какое-либо противоречие?
Конечно. Иначе можно было бы одной задачей считать любую композицию действий. Твоё право написать функцию, возвращающую минимальное простое число, большее своего аргумента и считать это одной задачей. Но когда ты будешь её реализовывать, наверняка декомпозируешь на несколько более простых. Неужели не будешь при этом их оформлять отдельными функциями и напишешь структурную простыню на пару экранов? Тут хотел ещё дальше написать, но передумал, потому что:
Цитата OpenGL @
Если про то, что хотел сделать программист c-style cast-ом, то он с обсуждением не связан.
Понятно. Не просто связан, а ответ на него является первопричиной данного холивара. Вынужден констатировать, что разговор окончен.

Автор: OpenGL 23.07.20, 19:12
Цитата Qraizer @
Иначе можно было бы одной задачей считать любую композицию действий.

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

Добавлено
Цитата Qraizer @
Не просто связан, а ответ на него является первопричиной данного холивара.

Не связан. C style casts легко провоцируют ошибки, в отличие от предмета спора. Ну или ты, как обычно, делаешь слишком тонкий намёк :D

Автор: Qraizer 23.07.20, 22:38
Цитата OpenGL @
Цитата Qraizer @
Иначе можно было бы одной задачей считать любую композицию действий.
Можно, вопрос лишь в том, имеет это какой-либо смысл или нет.
Дальше абзац ты не читал, я понял.
Цитата OpenGL @
C style casts легко провоцируют ошибки, в отличие от предмета спора.
C style casts не провоцирует ошибок. Предмет спора не провоцирует ошибок. Ты явно продемонстрировал, что не в теме.
Бинды rvalue на ссылки, вещественные типы и operator<<, operator>> и operator%, новый стиль кастов, отсутствие частичной специализации шаблонов функций, иммутабельность лямбд по умолчанию, невозможность перегрузки некоторых операций и ещё многое другое – всё это растёт из одного корня: философии, положенной в основу языка.
OpenGL, я сформулировал принцип, следуя которому язык развивался с начала 80-ых, ты упорно не хочешь его принять. Я задал простой вопрос, ответ на который легко отличает человека, знающего язык, от человека, понимающего язык. Трижды задал. Ты наотрез отказываешься отвечать. Я привёл ещё три примера следования этому же принципу, два из них ты проигнорировал, в третьем не увидел связи с – внезапно – исходным принципом. Я на что-то ещё и намекаю? Серьёзно, OpenGL?
Зачем ты продолжаешь писать в тему, которая тебе не интересна? Ок, считай, что я намекаю на то, что для Плюсов ты безнадёден. Ты сюда пришёл меня подоставать в надежде, что сорвусь?

Автор: OpenGL 24.07.20, 06:36
Цитата Qraizer @
Дальше абзац ты не читал, я понял.

Ты понял неверно :-? С чего ты вообще это взял? Написанное мной не противоречит этому абзацу дальше.

Цитата Qraizer @
C style casts не провоцирует ошибок. Предмет спора не провоцирует ошибок. Ты явно продемонстрировал, что не в теме.

c style casts-то не провоцирует? :lool:
Ладно, ты опять включил режим мара тьфу, мудрого сенсея то есть. Ну тебя нафиг, пожалуй.

Добавлено
Вообще, кто-нибудь понял, что Qraizer хотел этой своей аналогией со c style casts сказать?

Сообщения были разделены в тему "Обслуживание Мас"

Автор: Black_Dragon 20.08.20, 06:56
Я с вопросом к ТС-у.

А чего ты хочешь добиться, используя std::move?
CallbackType - это ссылка на функцию... технически - указатель.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)