为什么 C++23 std::move_only_function 没有推导指南?

康桓瑋*_*康桓瑋 13 c++ std-function c++17 c++23

C++23 引入了std::function的表兄弟std::move_only_function,就像它的名字一样,它是仅移动可调用对象的仅移动包装器(演示):

#include <functional>
#include <memory>

int main() {
  auto l = [p = std::make_unique<int>(0)] { };
  std::function<void(void)>           f1{std::move(l)}; // ill-formed
  std::move_only_function<void(void)> f2{std::move(l)}; // well-formed
}
Run Code Online (Sandbox Code Playgroud)

但与 不同的是std::function,该标准没有为其定义推导指南(演示):

#include <functional>

int func(double) { return 0; }
int main() {
  std::function f1{func};           // guide deduces function<int(double)>
  std::move_only_function f2{func}; // deduction failed
}
Run Code Online (Sandbox Code Playgroud)

禁止 CTAD 有理由吗?

T.C*_*.C. 11

像这样的类型擦除包装器move_only_function被设计用于 API 边界,其中类型是显式的,这使得 CTAD 的用处值得怀疑。

无论如何,这些可调用包装器的任何 CTAD 都必须受到相当的限制 - 它无法处理重载的函数或函数模板,这也意味着它无法处理通用 lambda,这对其实用性来说是一个相当重要的限制。std::function的 CTAD 还带有一个免责声明,即以后的标准可以更改推导的类型(我们还没有更改它,但我们也没有删除免责声明)。

并且move_only_function不仅仅是推导出返回值和参数类型。constnoexceptref 限定符都在起作用,这引入了新的设计问题。例如,演绎 from 是否可以int (*)(int)演绎int(int)?为什么不呢int(int) const——毕竟函数指针是可常量调用的?

如果事实证明需要 CTAD - 并且有人为其提出了一个好的设计 - 以后总是可以添加它。

我不知道这些观点是否都在 LEWG 讨论中提出(会议记录相当稀疏),但我认为它们足以证明不支持 CTAD 是合理的。