Как вывести тип возвращаемого значения из r-value ссылки? С++

Изучаю выведение типов в С++ и r-value ссылки. Есть шаблонный метод в классе, который принимает функцию в качестве аргумента:

template<typename Return>
Return then(Return&& function) const {
        
    static_assert(std::is_invocable_v<Return>, "Return cant be called.");
        
    return function();

}

И вызов метода:

int increment(int i) {
    return i + 1;
}

int main() {

    int a = myclass.then(increment);
    return 0;

}

Данный код не компилируется, поскольку нельзя присвоить значение int()() переменной a. Я порылся в отладчике и понял, что при передаче функции в качестве r-value ссылки тип Return автоматически становится int()() в данном случае, а я хочу, чтобы метод вернул результат функции. Возможно ли вывести тип возвращаемого значения?


Ответы (1 шт):

Автор решения: HolyBlackCat

Самый правильный вариант такой:

template <typename F>
decltype(auto) then(F &&function) const
{
    return function();
}

Или, еще лучше, с forward:

template <typename F>
decltype(auto) then(F &&function) const
{
    return std::forward<F>(function)();
}

Но раз вы не хотите, чтобы компилятор определял за вас тип, то так:

template <typename F>
auto then(F &&function) const -> decltype(function())
{
    return function();
}

Или, еще лучше, с forward:

template <typename F>
auto then(F &&function) const -> decltype(std::forward<F>(function)())
{
    return std::forward<F>(function)();
}

Здесь использован синтаксис trailing return type (замыкающий (т. е. написанный справа) возвращаемый тип). auto здесь не определяет тип сам, а просто означает, что настоящий тип написан справа, после ->.

Обычно тип функция(...) эквивалентно auto функция(...) -> тип. Но здесь мы вынуждены использовать второй вариант, потому что хотим использовать function в decltype, а левее списка параметров к нему нельзя обратится.

Есть другой вариант записи:

template <typename F>
decltype(std::declval<F>()()) func(F &&function) const
{
    return std::forward<F>(function)();
}

Записывая возвращаемый тип слева, мы не можем использовать function в decltype, но можем использовать F.

→ Ссылка