34

考虑这个来自错误 80985的示例:

template <class Func>
void call(Func f)
{
    f();
}

void func() noexcept { }

int main()
{
    call(func);
}

正如您所做的那样,在启用所有警告的情况下编译它会产生:

$ g++ -std=c++14 -Wall foo.cxx 
foo.cxx:2:6: warning: mangled name for ‘void call(Func) [with Func = void (*)() noexcept]’ will change in C++17 because the exception specification is part of a function type [-Wnoexcept-type]
 void call(Func f)
      ^~~~

我到底应该如何处理这个警告?解决方法是什么?

4

4 回答 4

21

对于警告消息,您可以做几件事。

用 禁用它-Wno-noexcept-type。在许多项目中,警告消息是无用的,因为生成的对象不可能与另一个期望它使用 GCC 的 C++17 名称修饰的对象链接。如果您没有使用不同的-std=设置进行编译,并且您没有构建静态或共享库,其中违规函数是其公共接口的一部分,则可以安全地禁用警告消息。

用 . 编译所有代码-std=c++17。警告消息将消失,因为该函数将使用新的损坏名称。

制作函数static。由于该函数不能再被另一个使用不同修饰函数的对象文件引用,因此不会显示警告消息。函数定义必须包含在所有使用它的编译单元中,但是对于像您的示例中这样的模板函数,这无论如何都是常见的。这也不适用于成员函数,static这意味着别的东西。

调用函数模板时,显式指定模板参数,以提供不具有异常规范的兼容函数指针类型。例如call<void (*)()>(func). 您也应该能够使用 cast 来执行此操作,但 GCC 7.2.0 仍然会生成警告,即使 using-std=c++17不会更改修饰。

当函数不是模板时,不要noexcept与函数类型中使用的任何函数指针类型一起使用。这一点和最后一点依赖于这样一个事实,即只有非抛出函数指针类型会导致命名重整更改,并且可以将非抛出函数指针分配 (C++11) 或隐式转换 (C++17) 以可能抛出函数指针。

于 2017-10-20T21:22:41.640 回答
2

我赞成罗斯对call<void (*)()>(func)解决方案的回答。它明确告诉编译器您希望为非noexcept函数类型实例化模板,并保证您的代码在 C++17 中的操作与在 C++14 中的操作完全相同。

更多的选择是:

(1) 将函数包装noexcept在 lambda 中(不是noexcept):

template <class Func>
void call(Func f)
{
    f();
}

void func() noexcept { }

int main()
{
    call([]() { func(); });
}

(2) 创建一个没有noexcept. 这最初需要更多的输入,但如果您有多个呼叫站点,它可以节省整体输入。这就是我最终在最初提示我提交 GCC 错误的代码中所做的事情。

于 2017-10-21T01:25:03.397 回答
1

除了已经说过的内容之外,我还找到了另一种在 GCC 7 中消除此警告的方法。显然,当且仅当第一次实例化call()涉及时,GCC 才会生成此警告noexcept。因此,解决方案是首先call()使用非noexcept函数进行实例化。

这个技巧也可以:

using dummy = decltype(call(std::declval<void(*)()>()));

在这种情况下,PS GCC 8.2.1 不会报告警告。

于 2019-02-26T17:53:40.523 回答
0

他们警告您的问题是,在 C++14 中,这将起作用:

void call(void (*f)())
{
    f();
}

void func() noexcept {}

int main(int argc, char* argv[])
{
    call(&func);
    return 0;
}

但在 C++17 中,您需要将声明更改call为:

void call(void (*f)() noexcept)
{
    f();
}

由于您已定义call为模板,因此您无需担心这一点。不过,它可能会给您带来问题,因为推断的类型正在发生变化,这通常不会发生。

例如,此代码将在 C++14 而不是 C++17 中编译:

void foo() noexcept {}
void bar()          {}

template <typename F>
void call(bool b, F f1, F f2)
{
    if (b)
        f1();
    else
        f2();
}

void foobar(bool b)
{
    call(b, &foo, &bar);
}

在 C++14 中foo和的类型bar相同,但在 C++17 中它们的类型不同,这意味着模板解析会失败。带有标志的 gcc 7.2 中的错误消息-std=c++1z是:

note:   template argument deduction/substitution failed:
note:   deduced conflicting types for parameter 'F' ('void (*)() noexcept' and 'void (*)()')

在您给出的示例中,没有问题,在 C++14 或 C++17 模式下编译也不会有问题。如果代码比这里的示例更复杂(例如,类似于我上面给出的示例),您可能会遇到一些编译器问题。看来你有一个最近的编译器;尝试编译-std=c++1z并查看是否有警告或错误。

于 2017-10-20T13:13:29.940 回答