为什么std::is_invocable不接受非类型模板参数

3

最近我偶然发现了std::is_invocable,它将被引入到C++17标准中。我想知道为什么它需要用户提供函数指针的类型,而不是直接提供函数指针本身,这可能更方便,特别是因为非类型模板参数现在可以是无限制的。

我的意思可以通过下面的例子来解释

void hello_world() {
    cout << "Hello world" << endl;
}
int main() {
    cout << std::is_invocable_v<decltype(hello_world)> << endl;
    // as opposed to being able to do
    // cout << std::is_invocable_v<hello_world> << endl;

    return 0;
}

因为将函数指针作为模板参数传递会导致鸡生蛋的问题,请参见https://dev59.com/hnM_5IYBdhLWcg3w6HvT(不认为这是一个重复问题)。您需要将函数类型定义为第一个模板参数,但您需要其余的模板参数来完成此操作。 - Sam Varshavchik
一个是类型,另一个是值。 - Kerrek SB
1
@SamVarshavchik 兄弟,你在说什么啊?C++17, template auto. - Barry
2个回答

8
因为你总是需要测试想要测试的可调用对象的类型,但你并不总是拥有它的值作为常量表达式。当然,当你拥有这个值时,你必须写出decltype(foo)而不是foo,但这似乎是一个相当小的负担,并且能够涵盖相当一部分的使用情况。我不确定是否值得增加一个template <auto F, class... Args> is_invocable的复杂度,只是为了让用户不必写出decltype

4
std::is_invocable 的主要用途是与类型和模板参数一起使用,而不仅仅是通过直接使用函数指针来使用。
让我们稍微修改一下您的代码并添加一个有用的案例:
void callF(F function, Args&& args) {
    std::invoke(function, std::forward<Args>(args)...);
}

// Later, in your main

callF(hello_world);

您希望过滤掉在调用时无效的函数。您可以像这样使用std::is_invokable

auto callF(F function, Args&& args) -> std::enable_if<std::is_invocable_v<F, Args...>> {
    std::invoke(function, std::forward<Args>(args)...);
}

如您所见,传递给 std::is_invocable 的参数类型反映了传递给 std::invoke 的参数。

作为额外奖励,不仅支持函数指针,还支持函数对象和成员函数指针。现在,您可以像这样使用 callF 函数:

callF([](int i){ /* ... */ }, 8);

struct Test { void test() {} };

Test t;

callF(&Test::test, t);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接