【问题标题】:Bizarre behavior of std::is_invocablestd::is_invocable 的奇怪行为
【发布时间】:2020-06-11 21:50:58
【问题描述】:

我对以下程序中的 std::is_invocable 有疑问:

#include <iostream>
#include <type_traits>

void g() {}

template<bool B, typename T>
void f(T t) {
    if constexpr (B)
        g(t);
}

int main() {
    std::cerr << std::boolalpha <<
        std::is_invocable_v<decltype(&f<true, int>), int> << std::endl;
}

我本来希望程序输出为 false,因为 f 无法实例化。但是,GCC(截至 10.0.1 20200224)无法编译,错误消息为

test.cpp: In instantiation of 'void f(T) [with bool B = true; T = int]':
test.cpp:14:51:   required from here
test.cpp:9:10: error: too many arguments to function 'void g()'
    9 |         g(t);
      |         ~^~~
test.cpp:4:6: note: declared here
    4 | void g() {}
      |  

和 Clang(从 11.0.0 开始)甚至打印 true

在这种情况下正确的行为是什么?

【问题讨论】:

    标签: c++ templates c++17 language-lawyer typetraits


    【解决方案1】:

    问题归结为:decltype(&amp;f&lt;true, int&gt;) 是否真的实例化了f&lt;true, int&gt;

    如果是,则程序不正确,因为该实例化不正确(不能按照要求使用 int 调用 g)。

    如果不是,那么invocable_v 的正确答案是true - 因为函数体上没有 SFINAE,并且函数的签名清楚地允许这种调用。

    似乎 gcc 认为是(和硬错误)而 clang 认为不是(并产生 true)。我认为clang在这里是正确的。我们的规则是:

    [temp.inst]/10:

    如果以涉及重载解析的方式使用函数模板或成员函数模板特化,则将隐式实例化特化的声明 ([temp.over])。

    [temp.inst]/11:

    实现不应隐式实例化函数模板、变量模板、成员模板、非虚拟成员函数、成员类、类模板的静态数据成员或 constexpr if 语句的子语句 ( [stmt.if]),除非需要这样的实例化。

    我们在这里确实涉及重载解析(函数地址),但这只是实例化函数模板的声明。我认为不需要实例化函数模板的定义f 返回void,而不是auto 之类的东西),所以 gcc 在这里过于急切地实例化它。

    也就是说,它当然不应该产生false

    【讨论】:

      猜你喜欢
      • 2011-03-06
      • 2013-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多