关于noexcept重载是否有意义?

时间:2018-01-18 19:05:50

标签: c++11 overloading overload-resolution noexcept c++17

我想了解noexcept功能。 我知道它可能会令人困惑,但除此之外,在可能的情况下,不能从调用函数中推断出它。

这是这种情况的一个非工作示例,

void f(){}
void f() noexcept{} // not allowed in c++

void g(){f();} // should call f
void h() noexcept{f();} // should call f noexcept
int main(){
    g();
    h();
}

如果调用函数(h)中没有try / catch块,那么编译器可以推断出有人对调用特定的f感兴趣。

此模式是否用于其他某种变通办法形式?

我能想象的是这样的事情,但它不是很通用:

template<bool NE> void F() noexcept(NE);

template<>
void F<true>() noexcept(true){}
template<>
void F<false>() noexcept(false){}

void g(){F<noexcept(g)>();} // calls F<false>
void h() noexcept{F<noexcept(h)>();} // call F<true>

有些人可能想知道为什么这样才有意义。 我的逻辑是C ++允许重载const,这是函数的参数和成员函数。 const成员函数更喜欢调用const成员重载。例如。

我认为noexcept函数调用noexcept“重载”是有意义的。特别是如果它们不是从try / catch块调用的。

2 个答案:

答案 0 :(得分:1)

我认为noexcept重载本身并没有多大意义。可以肯定的是,函数f也不例外,尤其是从h调用时,因为h需要捕获可能的异常并调用std::abort

但是,在noexcept上重载并不是一件好事。就像在标准库中禁用异常一样。我并不是说您不应该这样做,但是您会因此而失去功能。例如:如果索引无效,则std::vector::at抛出。如果您禁用例外,则没有其他选择可以使用此功能。

因此,如果您确实希望拥有2个版本,则可能需要使用其他替代方法来指示失败。 std :: optional,std :: expected,std :: error_code ...

即使您设法在noexcept上重载,您的函数也将具有不同的返回类型。作为您的框架中的用户,这不是我期望的。

因此,我认为重载是一种更好的方法,因此用户可以选择使用哪个变体,方法是显式使用布尔值std :: nothrow作为带有std :: error_code的参数输出参数。或者,也许您应该选择在库中使用的错误处理策略,然后将其强制执行给用户。

答案 1 :(得分:0)

这很有意义,

当然,从原则上讲这是合理的。例如,该函数的一个版本可以运行更快的算法,但是需要动态分配额外的暂存内存,而noexcept版本可以在堆栈上使用具有O(1)额外空间的较慢算法。

但无法解决过载...

您可能知道,从noexcept(false)函数中调用noexcept(true)函数是完全有效的。您只是冒terminate的风险,而不是抛出异常;有时-您没有冒任何风险,因为您已验证传递的输入不会触发异常。那么,编译器如何知道您要调用的函数的版本?对于另一个方向,同样的问题-也许您想从noexcept(true)函数中调用noexcept(false)函数?这也是允许的。

...--无论如何,大部分都是语法糖

使用C ++ 11,您可以编写:

#include <stdexcept>

template <bool ne2>
int bar(int x) noexcept(ne2);

template<> int bar<true>(int) noexcept { return 0; }
template<> int bar<false>(int) { throw std::logic_error("error!"); }

,这样编译就可以了:GodBolt

因此,您可以拥有两个函数,它们的参数相同且相同,只是w.r.t.不同。它们的noexcept值-但具有不同的模板参数。