У меня была эта простая функция:

template <class F>
F lockAndDo(F &&fct)
{
    std::unique_lock<std::mutex> lock{_mutex};
    return fct();
}    

Для которого я мог бы передать лямбда-функции, как это:

int value = LockAndDo([&] {
        return _collection.size();
    });

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

Затем я должен был сделать это разделение для пустых и не пустых лямбда-функций

template <class F>
F lockAndDoAndReturn(F &&fct)
{
    std::unique_lock<std::mutex> lock{_mutex};
    return fct();

}
template <class F>
void lockAndDo(F &&fct)
{
    std::unique_lock<std::mutex> lock{_mutex};
    fct();
}

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

2
Guerlando OCs 18 Авг 2019 в 08:34

2 ответа

Лучший ответ

Вам может понадобиться достаточно свежая версия C ++, которая, я надеюсь, является обычной практикой.

Я думаю, у вас есть несколько вариантов, в зависимости от поведения, которое вы хотите.

Если вы хотите вернуть по значению, используйте auto в качестве типа возврата:

template <class F>
auto lockAndDo(F &&fct)
{
    std::unique_lock<std::mutex> lock{_mutex};
    return std::invoke(fct);
}

Если вы разрешите возвращать ссылки, это не сработает, и вам понадобится decltype(auto).

template <class F>
decltype(auto) lockAndDo(F &&fct)
{
    std::unique_lock<std::mutex> lock{_mutex};
    return std::invoke(fct);
}

Имейте в виду, что теперь вам все еще требуются функции void. Это также можно решить:

template <class F, class ...A>
decltype(auto) lockAndDo(F &&fct, A &&...a)
{
    std::unique_lock<std::mutex> lock{_mutex};
    return std::invoke(fct, std::forward<A>(a)...);
}

И, как вы могли заметить, я использовал std::invoke во всех примерах, поскольку он поддерживает все виды вызовов. Это включает в себя указатели на функции-члены ...

Оставьте на усмотрение читателя: вы можете сделать свою функцию условно никакой, если только вы не заботитесь об этом.

4
JVApen 18 Авг 2019 в 05:55

Вы можете сделать тип возврата auto (или {{X1 }}, в зависимости от сценариев (см. дополнительную информацию на связанной странице), что будет определяться оператором return.

(since C++14) в типе возврата функции или лямбда-выражения: auto& f();. Тип возврата выводится из операнда его оператора возврата non-discarded (since C++17).

Например

template <class F>
auto // determined by the return type of `F`, including `void`
lockAndDo(F &&fct)
{
    std::unique_lock<std::mutex> lock{_mutex};
    return fct();
}

ПРЯМОЙ ЭФИР

4
songyuanyao 18 Авг 2019 в 06:19