gpt4 book ai didi

c++ - 为什么 GCC 的 std::function 不使用对按值传递的参数的右值引用在其内部委托(delegate)之间传递它们?

转载 作者:IT老高 更新时间:2023-10-28 22:30:30 26 4
gpt4 key购买 nike

首先,考虑以下代码:

#include <iostream>
#include <functional>

struct Noisy
{
Noisy() { std::cout << "Noisy()" << std::endl; }
Noisy(const Noisy&) { std::cout << "Noisy(const Noisy&)" << std::endl; }
Noisy(Noisy&&) { std::cout << "Noisy(Noisy&&)" << std::endl; }
~Noisy() { std::cout << "~Noisy()" << std::endl; }
};

void foo(Noisy n)
{
std::cout << "foo(Noisy)" << std::endl;
}

int main()
{
Noisy n;
std::function<void(Noisy)> f = foo;
f(n);
}

及其在不同编译器中的输出:

Visual C++ (see live)

Noisy()
Noisy(const Noisy&)
Noisy(Noisy&&)
foo(Noisy)
~Noisy()
~Noisy()
~Noisy()

Clang (libc++) (see live)

Noisy()
Noisy(const Noisy&)
Noisy(Noisy&&)
foo(Noisy)
~Noisy()
~Noisy()
~Noisy()

GCC 4.9.0 (see live)

Noisy()
Noisy(const Noisy&)
Noisy(Noisy&&)
Noisy(Noisy&&)
foo(Noisy)
~Noisy()
~Noisy()
~Noisy()
~Noisy()

也就是说,与 Visual C++(和 Clang+libc++)相比,GCC 执行一个移动/复制操作,让我们同意,并不是在所有情况下都有效(例如 std::array<double, 1000> 参数)。

据我了解,std::function需要对一些包含实际函数对象的内部包装器进行虚拟调用(在我的情况下为 foo )。因此,使用转发引用完美转发是不可能的(因为虚拟成员函数不能被模板化)。

但是,我可以想象实现可以std::forward内部所有参数,无论它们是按值传递还是按引用传递,如下所示:

// interface for callable objects with given signature
template <class Ret, class... Args>
struct function_impl<Ret(Args...)> {
virtual Ret call(Args&&... args) = 0; // rvalues or collaped lvalues
};

// clever function container
template <class Ret, class... Args>
struct function<Ret(Args...)> {
// ...
Ret operator()(Args... args) { // by value, like in the signature
return impl->call(std::forward<Args>(args)...); // but forward them, why not?
}

function_impl<Ret(Args...)>* impl;
};

// wrapper for raw function pointers
template <class Ret, class... Args>
struct function_wrapper<Ret(Args...)> : function_impl<Ret(Args...)> {
// ...
Ret (*f)(Args...);

virtual Ret call(Args&&... args) override { // see && next to Args!
return f(std::forward<Args>(args)...);
}
};

因为按值传递的参数只会变成右值引用(很好,为什么不呢?),右值引用将折叠并保留为右值引用,而左值引用将折叠并保留为左值引用 (see this proposal live) .这避免了任意数量的内部助手/委托(delegate)之间的复制/移动。

所以我的问题是,为什么 GCC 对按值传递的参数执行额外的复制/移动操作,而 Visual C++(或 Clang+libc++)却没有(因为它似乎没有必要)?我希望 STL 的设计/实现能够获得最佳性能。

请注意在 std::function 中使用右值引用签名,如 std::function<void(Noisy&&)> ,对我来说不是解决方案。


请注意,我并不是在寻求解决方法。我认为这两种可能的解决方法都不正确。

a) 使用 const 左值引用!

为什么不呢?因为现在当我调用 f右值:

std::function<void(const Noisy&)> f = foo;
f(Noisy{});

它禁止Noisy移动操作临时和强制复制。

b) 然后使用非常量右值引用!

为什么不呢?因为现在当我调用 f左值:

Noisy n;
std::function<void(Noisy&&)> f = foo;
f(n);

它根本不编译。

最佳答案

在 libstdc++ 中,std::function::operator() 不会直接调用函数,它会将任务委托(delegate)给帮助程序 _M_invoker。这种额外的间接级别解释了额外的拷贝。我没有研究代码,所以我不知道这个助手是否只是为了方便,或者它是否发挥了强大的作用。无论如何,我相信要走的路是在 gcc's bugzilla 中提交增强 PR .

关于c++ - 为什么 GCC 的 std::function 不使用对按值传递的参数的右值引用在其内部委托(delegate)之间传递它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26543242/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com