Вопрос по копированию/перемещению в новых плюсиках
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 10.10.21 19:17
Оценка:
Здравствуйте!

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

Решил сделать для таких случаев класс ScopeExec — принимает два функтора — обычно это лямбда по месту, первый функтор вызывается в конструкторе, второй — в деструкторе.
В принципе тот, который в конструкторе вызывается — ExecEnter — может и не нужен, просто для симметричности добавил.

В новых плюсах вроде компилятор сам умеет выводить параметры шаблона для класса, но я вопросом не владею, и сделал по старинке — объявил шаблонную makeScopeExec функцию

Запустил, вывелось:
  Вывод MSVC2017
Before creating scopeExec
ExecEnter
After creating scopeExec
ExecLeave
scopeExec scope leaved


Вроде все как надо. Но были большие подозрения, что это чисто случайно получилось.

Поэтому написал макрос с decltype'ми — там уж точно никаких лишних копирований и разрушений не должно быть. Правда, при первой пробе макрос я лямбды сразу в макрос передавал. Не собралось. Ну, ожидаемо.

Решил проверить на https://ideone.com/Rla7Jw — C++14 (gcc 8.3)

Не собралось. В принципе, я что-то подобное и ожидал, с MSVC явно на халяву прокатило. Правда, я думал, что будет собираться, но action'ы будут по два раза вызваны из-за копирования при возврате. Да, я в курсе, что в основном компиляторы такое умеют оптимизировать, но это именно что оптимизация, не факт, например, что в старичке Keil'е будет так же работать.

С макросом порешал — просто сначала создал auto переменные с лямбдами, и уже их передал в макрос. Это работает. И будет работать везде одинаково, тут я уверен.

Но интересно, как такое провернуть без макроса?

Попробовал на шару std::move при возврате — MSVC стал ругаться, что конструктор копирования приватный


  Код
//----------------------------------------------------------------------------
template< typename ExecEnter, typename  ExecLeave >
class ScopeExec
{
    ExecEnter   m_execEnter;
    ExecLeave   m_execLeave;

    ScopeExec();
    ScopeExec( const ScopeExec &scopeExec);

public:

    ScopeExec( const ExecEnter &execEnter, const ExecLeave &execLeave )
    : m_execEnter(execEnter)
    , m_execLeave(execLeave)
    {
        m_execEnter();
    }

    ~ScopeExec()
    {
        m_execLeave();
    }

}; // class ScopeExec

//----------------------------------------------------------------------------




//----------------------------------------------------------------------------
template< typename ExecEnter, typename  ExecLeave >
inline
ScopeExec<ExecEnter, ExecLeave> makeScopeExec( const ExecEnter &execEnter, const ExecLeave &execLeave )
{
    //return std::move( ScopeExec<ExecEnter, ExecLeave>( execEnter, execLeave ) );
    return ScopeExec<ExecEnter, ExecLeave>( execEnter, execLeave );
}

#define MAKE_SCOPE_EXEC( execEnter, execLeave ) \
             ScopeExec< decltype(execEnter), decltype(execLeave) >( execEnter, execLeave )


#include <iostream>

int main()
{

    { // ScopeExec test

        std::cout << "Before creating scopeExec" << std::endl;

        auto scopeExec = makeScopeExec( [&]() { std::cout << "ExecEnter" << std::endl; }
                                      , [&]() { std::cout << "ExecLeave" << std::endl; }
                                      );

        std::cout << "After creating scopeExec" << std::endl;

    }
    std::cout << "scopeExec scope leaved" << std::endl;


}
Маньяк Робокряк колесит по городу
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.