2017-10-17 281 views
30

考虑这个例子,从bug 80985处理gcc的noexcept类型警告

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) 
     ^~~~ 

正是我应该怎么办有这个警告?什么是修复?

+1

如果'call'完全在您的项目内部,则无关紧要。它只在两个不同的翻译单元使用它的情况下很重要,其中一个是用C++ 17编译的,而另一个不是。即使如此,由于'call'是一个模板函数,除了在最终的可执行文件中有额外的定义之外,它可能不会有太大的影响。 –

+2

@DanielH不是我的意思是说上面的Barry,但是如果你正在用-wError编译一个项目,那么这个“无害的警告”将导致程序根本无法编译,尽管是正确的。这很重要。 – markt1964

+1

@ markt1964在文章中只使用了'-Wall'。如果你用'-Werror'编译或者尝试避免编译错误(这是一个好主意),那么是的,你会遇到问题。可能最好的方法是加入'-Wno-noexcept-type',这取决于具体情况。 –

回答

18

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

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

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

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

在调用函数模板时,请指定模板参数,明确指定一个没有异常规范的兼容函数指针类型。例如call<void (*)()>(func)。你也应该可以使用cast来做到这一点,但GCC 7.2。即使使用-std=c++17也不会改变加固,0仍然会生成警告。

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

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); 
} 

foo C++ 14,类型和bar是相同的,但它们是不同的在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编译并查看是否有警告或错误。

+0

这不回答这个问题。我知道在类型系统中增加了'noexcept'。 – Barry

+0

我在最后加入了更多解释;希望能够澄清这个问题 – SJL

3

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

更多替代方案是:

(1)包裹在一个lambda的noexcept功能(这是不noexcept):

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

void func() noexcept { } 

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

(2)无noexcept创建一个单独的包装函数。这是最初的打字,但如果你有多个电话网站,它可以节省打字的整体。这是代码中的what I ended up doing,最初提示我提交GCC错误。