c++ - C++14 中 noexcept 说明符的奇怪行为

标签 c++ c++14 language-lawyer noexcept

我发现 noexcept 的奇怪行为C++14 中的运算符。
以下代码可以通过 gcc 和 clang(使用 --std=c++14 选项)很好地编译。

// test.cpp
#include <iostream>
#include <type_traits>

#if 1
#define TESTREF(X) X&&
#else
#define TESTREF(X) X const&
#endif

template <class F, class... Args>
struct is_noexcept_callable
    : public std::conditional_t<noexcept(std::declval<F>()(std::declval<Args>()...)), std::true_type, std::false_type> {};

template <
    class F,
    std::enable_if_t<is_noexcept_callable<F,int>::value,int> = 0
    >
int evalInt(int x, TESTREF(F) f) noexcept
{
    return static_cast<int>(f(x));
}

template <
    class F,
    std::enable_if_t<!is_noexcept_callable<F,int>::value,int> = 0
    >
int evalInt(int x, TESTREF(F) f)
{
    return static_cast<int>(f(x));
}

int id(int x) noexcept { return x; }
int thrower(int x) { throw(0); }

int main(int argc, char* argv[])
{
    std::cout << std::boolalpha
              << noexcept(evalInt(1,id))
              << std::endl;
    std::cout << std::boolalpha
              << is_noexcept_callable<decltype(thrower), int>::value
              << std::endl;
}

执行结果程序,但是根据编译器的不同,我得到了不同的结果:
$ g++ --std=c++14 test.cpp
$ ./a.out
true
false
$ clang++ --std=c++14 test.cpp
$ ./a.out
false
false

我不确定根据标准哪个是正确的。

更奇怪的是,如果我将上面代码中的第 5 行更改为 #if 0然后 gcc 将代码编译成另一个不同的程序:
$ ./a.out
true
true

如您所见,第二个值已更改。
但是,它仅取决于noexcept thrower规范宏不接触的功能。
对此有什么合理的解释,还是只是一个错误?

编辑

结果是在 Ubuntu 18.04(64 位)包存储库中使用 GCC 7.4.0 和 clang 6.0.0 获得的。

最佳答案

我只能在版本 8 之前的 GCC 中重现这个错误。行为的差异是由于 noexcept说明符是 GCC 7 的 C++14 版本(但不是 Clang 的)中函数类型的一部分,尽管这是 C++17 的特性。如果我们添加 is_noexcept_callable 的部分特化,就可以看到这一点。 :

template <class... Args>
struct is_noexcept_callable<int(&)(int), Args...>
    : public std::false_type {};

template <class... Args>
struct is_noexcept_callable<int(int), Args...>
    : public std::false_type {};

这突然yields two false s : GCC 保留了 noexcept函数类型上的属性,但在模板参数推导期间显式忽略它们,以便选择上述特化,尽管错误消息显示 noexcept如果我们删除定义:
prog.cc:30:5: note:   template argument deduction/substitution failed:
prog.cc:28:22: error: incomplete type 'is_noexcept_callable<int (&)(int) noexcept, int>' used in nested name specifier

为什么TESTREF的定义影响 is_noexcept_callable ?

你问题的第二部分更微妙。在这里,问题是is_noexcept_callable已经使用相关类型实例化 int(int) [noexcept]在您使用它之前 main ,但它没有附加,所以 is_noexcept_callable<int(int), int>::value 的结果固定为true。
decltype(id)int(int) [noexcept] ,其中 [noexcept]是我的符号来表达附加到函数类型的 GCC 的 transient 异常规范。因此evalInt(1,id)导致实例化
  • is_noexcept_callable<F,int>哪里 F = int(&)(int) [noexcept] TESTREF = X&&
  • F = int(int) [noexcept] TESTREF = X const&

  • 因此,当您禁用 if 指令的第一个分支时,is_noexcept_callable<int(int),int>::value == true之后持有 noexcept(evalInt(1,id))已处理,因为 id是 noexcept 并且这会沿着实例化链向下传播。

    因此,以下打印两个错误:
    int main(int argc, char* argv[])
    {
        std::cout << std::boolalpha
                  << noexcept(evalInt(1,thrower))
                  << std::endl;
        std::cout << std::boolalpha
                  << is_noexcept_callable<decltype(thrower), int>::value
                  << std::endl;
    }
    

    演示:https://wandbox.org/permlink/YXDYfXwtEwMQkryD

    关于c++ - C++14 中 noexcept 说明符的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60201135/

    相关文章:

    c++ - 作为鼠标的相机输入(运动跟踪)

    c++ - 如何抽象出数学软件中的整数类型

    c++ - 读取文件并将其存储在 C++ 中的 protected 变量中

    c++ - clang 和 gcc 为相同的代码生成不同的逻辑。哪个是对的?

    c++ - 模板函数参数的默认值数据类型与实例化数据类型不同

    c++ - 是否调用函数指针来生成代码未定义的行为?

    c++ - 使用 C++ 在数组中查找不同的元素

    c++ - IPv6 下的网络字节顺序是否毫无意义?

    c++ - lambda 的仅类型模板参数

    c++ - 是否可以在 Xcode 5.1 中使用 std::make_unique?