转发与不转发传递给包装器的函数

Forwarding vs not Forwarding a function passed to a wrapper

我的问题是完美转发传递给我们包装器的函数有什么好处。

template<typename T, typename ...U>
    auto time_function(T&& func, U&& ...args)
{
    std::cout<<"timing void function"<<std::endl;
    //std::forward<T>(func)(std::forward<U>(args)...); //this vs next one
    func(std::forward<U>(args)...);

    std::cout<<"timing over"<<std::endl;
}

在参数转发的情况下,很明显转发保留了参数的左值与右值。不过调用前转发func有没有用?

假设我将临时仿函数和普通函数都传递给 time_function 包装器。

假设我有一个有状态的赋值器仿函数。分配器将被多次重用,因此每次都必须复制值。但是,如果分配器是右值,它可以只移动值:

struct Assignor {
    std::string value;
    void operator()(std::string& dest) const &
    {
        dest = value;
    }
    void operator()(std::string& dest) &&
    {
        dest = std::move(value);
    }
};

现在,完美转发对右值产生影响:

Assignor f{std::string(10000, 'X')};
std::string a, b, c;

time_function(f, a);                                 // copies the string
time_function(std::move(f), b);                      // should move the string
                                                     // but copies if you don't std::forward
time_function(Assignor{std::string(10000, 'Y')}, c); // same

(这只是一个如何优化函子的例子w.r.t。值类别。我知道它看起来有点做作,但人们总是会想出有创意的想法。)


顺便说一句,你应该使用 std::invoke 而不是直接调用 ():

std::invoke(std::forward<T>(func), std::forward<U>(args)...);

除了 L.F. 的回答之外,我想对一个潜在问题做一个小说明,这个问题很明显但仍然可以被忽略:有时它通过通用引用传递功能对象并将它们作为右值调用是危险的。假设不是

void time_function(T&& func, ...)

调用 std::forward<T>(func)(...) 一次,你有

void for_each(T&& func, ...)

可能会多次调用 std::forward<T>(func)(...)。然后在第一次调用之后,所有进一步的调用都不安全。在 L.F. 的示例中,这对应于 "moved-from" 状态的多个分配:在第一次分配后, std::string value 成员将被保留在 "valid but unspecified state" 中,以后的分配将不会执行预期的操作(尽管它们不会是 UB)。如果 Assignor 没有 operator()&& 重载,问题就不会出现。