该noexcept关键字可以适当地应用于许多功能签名,但我不能确定何时我应该考虑在实践中使用它.根据我到目前为止所读到的内容,最后一分钟的添加noexcept似乎解决了移动构造函数抛出时出现的一些重要问题.但是,我仍然无法对一些实际问题提供令人满意的答案,这些问题使我首先要了解更多信息noexcept.
我知道永远不会抛出许多函数的例子,但编译器无法自行确定.noexcept在所有这些情况下我应该附加到函数声明吗?
不得不考虑我是否需要noexcept在每个函数声明之后追加,这将大大降低程序员的工作效率(坦率地说,这将是一个痛苦的屁股).对于哪些情况,我应该更加小心使用noexcept,以及在哪些情况下我可以使用暗示noexcept(false)?
我何时才能真实地期望在使用后观察到性能提升noexcept?特别是,给出一个代码示例,C++编译器在添加之后能够生成更好的机器代码noexcept.
就个人而言,我关心的是noexcept因为编译器提供了更大的自由来安全地应用某些类型的优化.现代编译器是否noexcept以这种方式利用?如果没有,我可以期待他们中的一些人在不久的将来这样做吗?
有没有之间的任何其他差别throw()和noexcept除了被检查的运行时分别编译时间?
维基百科C++ 11文章表明不推荐使用C++ 03 throw说明符.
为什么这样,noexcept有足够的能力在编译时覆盖所有这些?
想象一下,我正在写一些容器模板或其他东西.现在是时候专注std::swap于它了.作为一个好公民,我会通过这样的方式启用ADL:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y) {
using std::swap;
swap(x.something_that_is_a_T, y.something_that_is_a_T);
}
Run Code Online (Sandbox Code Playgroud)
这非常整洁.直到我想添加一个异常规范.我swap是noexcept只要调换T的noexcept.所以,我会写一些类似的东西:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))
Run Code Online (Sandbox Code Playgroud)
问题是,swap在那里需要ADL发现swap或std::swap.我该如何处理?
我正在尝试创建一个源自std::exception和覆盖的自定义异常what().起初,我这样写:
class UserException : public std::exception
{
private:
const std::string message;
public:
UserException(const std::string &message)
: message(message)
{}
virtual const char* what() const override
{
return message.c_str();
}
};
Run Code Online (Sandbox Code Playgroud)
这在VS2012中工作正常,但它不能在GCC 4.8中编译-std=c++11:
错误:'virtualos char*UserException :: what()const'的looser throw说明符
所以我补充说noexcept:
virtual const char* what() const noexcept override
Run Code Online (Sandbox Code Playgroud)
这在GCC中工作正常,但它不能在Visual Studio中编译(因为VS 2012不支持noexcept):
错误C3646:'noexcept':未知的覆盖说明符
建议的方法是什么?我希望使用两个编译器编译相同的代码,并且我正在使用C++ 11功能,因此我无法使用不同的编译-std.
为什么std::vector的operator[],front和back成员函数没有被指定为noexcept?
以下来自Scott Meyers的新C++ 11书中的草稿(第2页,第7-21行)
展开调用堆栈和可能展开调用堆栈之间的区别对代码生成产生了惊人的巨大影响.在noexcept功能,优化不需要保持运行栈在unwindable状态,如有异常会向外传播的功能,也不是他们必须确保在noexcept函数对象在施工相反的顺序应该例外离开此功能被破坏.结果是更多的优化机会,不仅在noexcept函数的主体内,而且在调用函数的站点.这种灵活性仅适用于noexcept功能.具有"throw()"异常规范的函数缺少它,没有异常规范的函数也没有.
相比之下,部分5.4的"关于C++的性能技术报告"描述了"代码"和实施异常处理的"表"的方式.特别是,当没有抛出异常并且只有空间开销时,"table"方法被显示没有时间开销.
我的问题是这个 - 斯科特迈尔斯在谈论解散和可能解散的时候谈到了什么优化?为什么这些优化不适用throw()?他的评论是否仅适用于2006 TR中提到的"代码"方法?
默认的放置new运算符在18.6 [support.dynamic]1中声明,带有非抛出异常规范:
void* operator new (std::size_t size, void* ptr) noexcept;
Run Code Online (Sandbox Code Playgroud)
这个函数没有任何作用,除非return ptr;它是合理的noexcept,但是根据5.3.4 [expr.new]15这意味着编译器必须检查它在调用对象的构造函数之前不返回null:
-15-
[ 注意:除非使用非抛出异常规范(15.4)声明分配函数,否则它表示无法通过抛出std::bad_alloc异常来分配存储(第15,18.6.2.1节); 否则返回非空指针.如果使用非抛出异常规范声明分配函数,则返回null以指示无法分配存储,否则返回非空指针.-end note ]如果分配函数返回null,则不进行初始化,不应调用解除分配函数,并且new-expression的值应为null.
在我看来(特别是对于放置new,而不是一般)这个空检查是一个不幸的性能命中,尽管很小.
我一直在调试一些代码,其中new在一个性能敏感的代码路径中使用了放置,以改进编译器的代码生成,并在程序集中观察到null检查.通过提供new使用抛出异常规范声明的特定于类的放置重载(即使它不可能抛出),删除了条件分支,这也允许编译器为周围的内联函数生成更小的代码.说放置new函数的结果可能会抛出,即使它不能,也是可测量的更好的代码.
所以我一直想知道是否真的需要进行空检查new.它返回null的唯一方法是将它传递给null.尽管写下来是可能的,而且显然是合法的:
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
Run Code Online (Sandbox Code Playgroud)
我不明白为什么这将是有益的,我认为它会更好,如果程序员有明确使用放置前检查null new如
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
Run Code Online (Sandbox Code Playgroud)
有没有人需要放置new来正确处理空指针的情况?(即不添加作为ptr有效内存位置的显式检查.)
我想知道禁止将空指针传递给默认的放置 …
constexpr说明noexcept符是否意味着函数的说明符?对类似问题的回答对说明者说"是" inline,但Eric Niebler的文章让我想知道对当前问题的可能答案.在我看来,答案取决于使用constexpr函数的上下文:是常量表达式上下文还是运行时上下文,即在编译时是否已知函数的所有参数.
我希望答案是"是",但简单的检查表明情况并非如此.
constexpr
bool f(int) noexcept
{
return true;
}
constexpr
bool g(int)
{
return true;
}
static_assert(noexcept(f(1)));
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour
#include <cassert>
#include <cstdlib>
int
main(int argc, char * [])
{
assert(noexcept(f(argc)));
assert(noexcept(g(argc)));
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud) 从bug 80985开始考虑这个例子:
template <class Func>
void call(Func f)
{
f();
}
void func() noexcept { }
int main()
{
call(func);
}
Run Code Online (Sandbox Code Playgroud)
正如您所做的那样,在启用所有警告的情况下进行编译会产生:
$ 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)
^~~~
Run Code Online (Sandbox Code Playgroud)
我应该怎么做这个警告呢?修复是什么?
最近的一个问题(特别是我对它的回答)让我想知道:
在C++ 11(和更新的标准)中noexcept,除非另有说明(即noexcept(false)),否则析构函数总是隐式的.在这种情况下,这些析构函数可能合法地抛出异常.(请注意,这仍然是你应该知道你在做什么 - 情况!)
但是,所有重载
std::unique_ptr<T>::reset()都被声明为noexcept(参见cppreference),即使析构函数T不是,如果析构函数在此期间抛出异常,也会导致程序终止reset().类似的事情适用于std::shared_ptr<T>::reset().
为什么reset()总是noexcept,而不是条件noexcept?
noexcept(noexcept(std::declval<T>().~T()))如果析构函数T是noexcept ,应该可以声明它使得它完全没有.我在这里遗漏了什么,或者这是标准中的疏忽(因为这无疑是一个高度学术化的情况)?