【问题标题】:Why does `std::all_of` not use `std::invoke`?为什么`std::all_of`不使用`std::invoke`?
【发布时间】:2025-12-17 00:10:01
【问题描述】:

在类似的问题(例如here)中已经指出,您不能将类方法指针作为谓词传递给std::all_of

但是,在 C++17 中,我们有 std::invoke,这应该使 std::all_of 和类似函数更容易接受成员函数(甚至成员变量)指针。

更具体地说,以下内容无法在 GCC 9.2 上编译:

#include <algorithm>
#include <vector>

struct S {
    bool check() const { return true; }
};

int main() {
    std::vector<S> vs;
    std::all_of(vs.begin(), vs.end(), &S::check);
}

这个Godbolt link 包含一些示例代码和使用调用的all_of 的玩具版本。

为什么会有这个限制?我错过了什么吗?我想当std::invoke被标准化时,它也应该被应用到适当的STL函数中。

【问题讨论】:

  • 查看 Godbolt 链接。调用成员函数是invoke的目的之一。
  • 它以*it作为成员对象调用,其中itall_of实现中的当前迭代器。
  • 我现在明白你的意思了。错过了S的向量。
  • @StoryTeller 没问题。
  • 更接近您的问题,我认为您的代码可以工作。可能是实现中的错误?

标签: c++ stl c++17


【解决方案1】:

原因 1:从来没有人提议过。P0312R1 提议在语言中使指向成员函数的指针可调用,但被拒绝了(对进行此更改没有达成共识)。

原因 2:使用 lambda(以及之前的 std::bind),几乎没有动力。如果S 是标准定义的类型(例如vector),则由于其他原因,成员指向指针选项将是非法的。

std::all_of(vs.begin(), vs.end(), [](auto const& s) {return s.check();});

【讨论】:

  • bind相比,我认为std::mem_fn是一个更令人信服的理由(就可读性而言)。
  • 很高兴你提到了mem_fn。你是对的,它是令人信服的。出于某种原因,我个人总是很难使用该系列的功能。我发现 lambda 语法最易读。但我坦率地承认,我的偏好非常主观。
  • 原因3:它不应该使用invoke,我们应该只指向可调用的成员(我觉得有义务在任何相关的地方发表这个评论)。
  • 感谢您的回答,我知道 lambdas。然而,必须编写一个 lambda 来表达语言中已经存在的概念(即方法指针)感觉有点傻。更不用说添加的样板以及这是 std::invoke 的确切用例这一事实。
  • 顺便问一下:如果S 是标准定义的类型,为什么指向成员的指针是非法的?
【解决方案2】:

下一代算法(std::ranges 命名空间中的算法)接受使用std::invoke 调用的谓词,完全按照您的建议 (https://godbolt.org/z/uaPoJf):

std::ranges::all_of(vs.begin(), vs.end(), &S::check);

或更短(https://godbolt.org/z/_qiO8G):

std::ranges::all_of(vs, &S::check);

此外,它们接受一个称为“投影”的附加参数,这是一个传递给算法的一元变换函数,在算法对元素进行操作之前应用于每个元素。例如(https://godbolt.org/z/gWY-OR):

std::ranges::all_of(vs, std::logical_not(), &S::check);

您已经可以通过 Casey Carter 的 cmcstl2 或 Eric Niebler 的 range-v3 使用以上所有功能。

【讨论】: