Совершенные переадресации вызываемых объектов в функциях более высокого порядка

Я писал такие функции более высокого порядка:

template<typename F, typename... Args>
void doStuff(F f, Args&&... args)
{
    // ...
    f(std::forward<Args>(args)...);
    // ...
}

Или замените F f на F&& f.

Но после того, как я узнал о ref-qualifiers (ouch), все усложнилось. Представьте себе класс функтора:

struct Foo {
    void operator()(...) &;
    void operator()(...) &&;
};

Тогда мои предыдущие реализации doStuff будут когда-либо вызывать метод &, так как параметры всегда lvalues.

Я думаю, что способ решить это - реализовать doStuff следующим образом:

template<typename F, typename... Args>
void doStuff(F&& f, Args&&... args)
{
    // ...
    std::forward<F>(f)(std::forward<Args>(args)...);
    // ...
}

Это также означает, что std::result_of возможно реализовано. Я хочу знать, есть ли какой-то недостаток этой реализации, т.е. Следует ли мне заменять все мои HOF-реализации?

Ответ 1

std::forward - это условное значение r-значения, и точка, как обычно, крадет кишки из умирающих объектов. В этом отношении нет разницы между functors и другими объектами. Нижняя сторона такая же, как при любом использовании std::forward; если вы не готовы украсть эти кишки, вы не должны.

#include <iostream>
#include <vector>

struct Foo {
    std::vector<int> vec = {1,2,3};
    std::vector<int>& operator()(...) & {
        return vec;
    }
    std::vector<int> operator()(...) && {
        return std::move(vec);
        // Clearly, don't rely on 'vec' here...
    }
};

Foo getFoo(){
    return {};
}

int main() {
    auto foo = getFoo();
    auto vec1=foo();
    auto vec2=getFoo()();
}

Ясно, что не делайте такие вещи:

auto vec3 = std::move(foo)();
vec4 = foo();

по тем же причинам.