如何使用概念约束仿函数的参数?

How do I constrain the parameter(s) of a functor using concepts?

我有以下概念:

template <typename T>
concept FunctorInt = requires(T a, int b) {
    a.operator()(b); //require the () operator with a single int parameter.
};

我在以下函数中使用它:

template <FunctorInt functor_t>
void for_each_sat_lit(const int ClauseIndex, functor_t functor) {
    auto LitIndex = -1;
    uint32_t Count = h_SatisfiedLitCount[ClauseIndex];
    if constexpr (!satlit) { Count = h_LiteralsInClauseCount[ClauseIndex] - Count; }
    for (uint32_t dummy = 0; dummy < Count; dummy++) {
        LitIndex = NextSetBit(h_SATLiteralBits[ClauseIndex], LitIndex);
        functor(LitIndex); //LitIndex must be an int.
    }
}

编译通过。但是,当我尝试通过将概念更改为

来破坏代码时
template <typename T>
concept FunctorInt = requires(T a, float b) {
    a.operator()(b); //I intend to require the () operator with a single float parameter.
};

它仍然可以编译,这意味着它根本没有约束仿函数。

如何限制仿函数使其只能有一个 int 参数?

MSVC:concepts.cpp

#include <concepts>

template <typename T>
concept FunctorInt = requires(T a, int b) {
    a.operator()(b); //require the () operator with a single int parameter.
};

template <typename T>
concept FunctorFloat = requires(T a, float b) {
    a.operator()(b); //require the () operator with a single float parameter.
};


void Loop(FunctorInt auto functor) {
    for (auto i = 0; i < 10; i++) {
        functor(i);
    }
}

void LoopShouldNotCompile(FunctorFloat auto functor) {
    for (auto i = 0; i < 10; i++) {
        functor(i); //<< i is not a float.
    }
}

int main(const int argc, const char* argv[]) {
    Loop(                [](int   a){ printf("%i", a); });
    LoopShouldNotCompile([](float a){ printf("%f", a); });
}

如果我使用 std::invocable 更改 FunctorIntFunctorFloat 的定义,同样的问题仍然存在:

concept FunctorInt = std::invocable<int>;

concept FunctorFloat = std::invocable<float>;

一切仍然编译,而它应该在 LoopShouldNotCompile.

上给出编译错误

更新:

我决定:

template <typename T>
concept FunctorInt = 
    requires() { [](void(T::*)(int) const){}(&T::operator()); }
 || requires() { [](void(T::*)(int)      ){}(&T::operator()); };

它创建了一个只允许单个 int 参数并且不关心常量正确性的仿函数。

如果您想阻止函数接受您想要的任何其他类型,您可以使用类型匹配函数模板...例如

#include <concepts>

template <typename T>
requires std::same_as<T,float>
void func([[maybe_unused]]T f) {}

int main() {
    //func(1); // doesn't compile
    func(1.0f); // works
    //func(1.0); // NB: fails again, because float!=double
}

编辑:更短

void func([[maybe_unused]]std::same_as<float> auto f) {}

你的概念检查是否可以用 int 调用类型 但你无法控制之前发生的 promotion/conversion。

您可以检查 T::operator() 的签名,但是之前的有效情况(如重载、模板函数、没有确切的函数但相似(constvolatile、...) ) 可能不再有效。

例如:

template <typename T>
concept FunctorInt = requires() {
    [](void(T::*)(int) const){}(&T::operator());
};

void Loop(FunctorInt auto functor) { /**/ }

int main() {
    Loop([](int   a){ printf("%i", a); });
    Loop([](float a){ printf("%f", a); }); // Not compile
}

Demo