处理gcc的noexcept-type警告

时间:2017-10-17 20:13:52

标签: c++ g++ c++17 noexcept gcc7

bug 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 个答案:

答案 0 :(得分:19)

您可以对警告信息做几件事。

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

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

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

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

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

答案 1 :(得分:3)

我赞成罗斯对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错误的代码中的what I ended up doing

答案 2 :(得分:1)

除了已经说过的话,我还找到了另一种方法来消除GCC 7中的此警告。显然,只有当call()的第一个实例涉及{{1}时,GCC才会生成此警告。 }。因此,一种解决方案是首先使用非noexcept函数实例化call()

此技巧还可以:

noexcept

P.S。在这种情况下,GCC 8.2.1不会报告警告。

答案 3 :(得分: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中,foobar的类型相同,但它们在C ++ 17中有所不同,这意味着模板解析将失败。带有标志-std=c++1z的gcc 7.2中的错误消息是:

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

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