Skip to content

std::function 的显式右值模板参数

约 368 字大约 1 分钟

技术随笔cpp

2025-06-09

问题

在修改一个 仅可移动 的类 class Resources 相关调用接口遇到了些问题。

在移除了接口中不必要的移动语义参数的时候,发现修改后 std::function<Resources> 类型的 callback 编译出错了。

class Resources 和 callback 的简要声明如下:

class Resources final : boost::noncopyable {
 public:
  using output_callback_t = std::function<void(Resources)>;
  explicit Resources(Resources&&) noexcept; // 移动构造
  Resources(...); // 省略

private:
  std::unique_ptr<Data> _resource;
};

看了下 GCC 中 std::function::operator () 的实现:

    /**
      *  @brief Invokes the function targeted by `*this`.
      *  @returns the result of the target.
      *  @throws `bad_function_call` when `!(bool)*this`
      *
      *  The function call operator invokes the target function object
      *  stored by `this`.
      */
    _Res
    operator()(_ArgTypes... __args) const
    {
if (_M_empty())
  __throw_bad_function_call();
return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
    }

std::function 在调用时使用 std::function 构造时的 _ArgTypes 作为 forward 模板参数,而非直接使用 operator() () 的参数。

这导致 std::function::operator () 在调用函数对象的时候丢失了 Resources 的右值性。

因此,此时形如如下的代码

auto foo() {
  output_callback_t cb = [](Resources res) {};
  Resources curr_res{};
  cb(std::move(curr_res)); // 这里会编译出错~
}

正确做法

在声明仅可移动的对象作为参数的 std::function callback 的时候,需要显式将参数声明为右值引用,在此例中则为:

using output_callback_t = std::function<void(Resources&&)>