c++ - std::invocable 和 std::regular_invocable 概念之间有什么区别?

标签 c++ generic-programming c++20

std::invocable有什么区别和 std::regular_invocable ?根据来自的描述
https://en.cppreference.com/w/cpp/concepts/invocable我希望 std::regular_invocable概念不允许在调用时更改函数对象的状态(或至少调用的结果应始终返回相同的结果)。
为什么下面的代码会编译?
用命令编译:g++-10 -std=c++2a ./main.cc .

#include <iostream>
#include <concepts>

using namespace std;

template<std::regular_invocable F>
auto call_with_regular_invocable_constraint(F& f){
    return f();
}

template<std::invocable F>
auto call_with_invocable_constraint(F& f){
    return f();
}

class adds_one {
    int state{0};
public:
    int operator()() { 
        state++;
        return state; 
    }
};

int main()
{
    auto immutable_function_object([]() { return 1; });
    adds_one mutable_function_object;

    // I would expect only first three will be compiled and the last one will fail to compile because the procedure is
    // not regular (that is does not result in equal outputs given equal inputs).
    cout << call_with_invocable_constraint(immutable_function_object) << endl;
    cout << call_with_invocable_constraint(mutable_function_object) << endl;
    cout << call_with_regular_invocable_constraint(immutable_function_object) << endl;
    cout << call_with_regular_invocable_constraint(mutable_function_object) << endl; // Compiles!
}
程序的输出:
1
1
1
2

最佳答案

来自 the reference :

Notes

The distinction between invocable and regular_invocable is purely semantic.


这意味着编译器无法通过概念系统强制区分,因为它只能检查句法属性。
从介绍到concepts library :

In general, only the syntactic requirements can be checked by the compiler. If the validity or meaning of a program depends whether a sequenced of template arguments models a concept, and the concept is satisfied but not modeled, or if a semantic requirement is not met at the point of use, the program is ill-formed, no diagnostic required.


假设,我们可以这样写:
template< class F, class... Args >
concept regular_invocable = invocable<F, Args...> &&
  requires(F&& f, Args&&... args) {
    auto prev = f;
    std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    assert(f == prev);
    // TODO assert that `args` are unchanged
    // TODO assert that invoking `f` a second time gives the same result
  };
然而,这实际上不会测试断言是否成立,因为 requires子句不在运行时调用,而只在编译时检查。

关于c++ - std::invocable 和 std::regular_invocable 概念之间有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64739274/

相关文章:

c++ - 无法将结构数组传递给函数

c++ - Char 数组上的累积按位或

fortran - 无需预处理即可通过子例程实现打印语句的强大功能?

c++ - std::future::then()和并发TS在哪里?

c++ - Cocos2dx中通过plist文件加载动画

c++ - 使用 MFC 在 C++ 中将图像插入到 RTF 数据中

Java:扩展数组大小,似乎无法将所有值保留在原始位置

C++ "triangle"(而不是菱形)继承

c++ - std::list<std::reference_wrapper<T>> 的基于概念限制范围的 for 循环

c++ - 您如何使用运算符 | 创建与现有 View 交互的自己的 View ?