我编写了以下代码来测试noexcept函数调用之间的传播,它似乎不像我想象的那样工作.在GCC 4.7.2中,可以有效地测试一个函数,noexcept不仅仅是直接或作为模板特化参数传递; 但不是作为参数传递给模板化函数,或作为函数指针传递给普通函数 - 即使该函数声明其形式参数为noexcept.这是代码:
#include <iostream>
#define test(f) \
std::cout << __func__ << ": " #f " is " \
<< (noexcept(f()) ? "" : "not ") \
<< "noexcept\n";
template <void(*f)()>
static inline void test0() {
test(f);
}
template <typename F>
static inline void test1(F f) {
test(f);
}
static inline void test2(void(*f)()) {
test(f);
}
static inline void test3(void(*f)()noexcept) {
test(f);
}
void f1() {}
void f2() noexcept {} …Run Code Online (Sandbox Code Playgroud) noexcept 或者没有复制构造函数(仅移动类型)我发现这个相当令人惊讶,因为只有移动类型的投掷移动器仍然会有这个移动 - 由使用的代码调用move_if_noexcept.
有没有给出一个彻底的理由呢?(可能直接或在N2983之间?)
不编码会不会更好,而不是仍然不得不面对不可恢复的移动场景?vectorN2983中给出的示例很好:
void reserve(size_type n)
{
... ...
new ((void*)(new_begin + i)) value_type( std::move_if_noexcept( (*this)[i]) ) );
}
catch(...)
{
while (i > 0) // clean up new elements
(new_begin + --i)->~value_type();
this->deallocate( new_begin ); // release storage
throw;
}
*!* // -------- irreversible mutation starts here -----------
this->deallocate( this->begin_ );
this->begin_ = new_begin;
... ...
Run Code Online (Sandbox Code Playgroud)
标记线中给出的注释实际上是错误的 …
无论我在互联网上阅读什么,强烈建议如果我希望我的班级能够很好地使用std::vector(即从我的班级移动语义std::vector),我应该将构造函数移动为"noexcept"(或noexcept(true)).
std::vector使用它,即使我将其标记noexcept(false)为实验?#include <iostream>
#include <vector>
using std::cout;
struct T
{
T() { cout <<"T()\n"; }
T(const T&) { cout <<"T(const T&)\n"; }
T& operator= (const T&)
{ cout <<"T& operator= (const T&)\n"; return *this; }
~T() { cout << "~T()\n"; }
T& operator=(T&&) noexcept(false)
{ cout <<"T& operator=(T&&)\n"; return *this; }
T(T&&) noexcept(false)
{ cout << "T(T&&)\n"; }
};
int main()
{
std::vector<T> t_vec;
t_vec.push_back(T());
}
Run Code Online (Sandbox Code Playgroud)
T()
T(T&&) …Run Code Online (Sandbox Code Playgroud) 我有一个operator[]为我的班级,它所做的只是呼吁std::unique_ptr::operator[]一个unique_ptr成员.相关部分就是这样:
template <typename T> struct Foo {
T& operator [](const size_t pos) const noexcept
{
return data_[pos];
}
std::unique_ptr<T[]> data_;
};
Run Code Online (Sandbox Code Playgroud)
我已将运营商标记为noexcept.然而,unique_ptr::operator[]是不是 noexcept.我无法找出原因,以及我是否可以假设它永远不会抛出.unique_ptr::operator[]本身没有列出文档中的任何异常(cppreference和MSDN声称它没有定义它可能抛出的任何异常列表.)
所以我假设缺失noexcept可能是:a)错误,或b)运算符访问的基础数据类型可能抛出.选项a会很好,因为这意味着我可以标记自己的运算符noexcept.选项b很难理解,因为所有操作员都会获得参考,并且它不会调用任何东西.
所以,长话短说,是否有任何unique_ptr::operator[]抛出的可能性,从noexcept函数中调用它是否安全?
我很难理解这一点.
double compute(double x, double y) noexcept
{
if (y == 0)
throw std::domain_error("y is zero");
return x / y;
}
Run Code Online (Sandbox Code Playgroud)
这在clang中编译得很好(我没有检查过gcc),但这对我来说似乎是胡说八道.为什么编译器允许noexcept函数包含throw语句?
我试图了解 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();
}
Run Code Online (Sandbox Code Playgroud)
如果调用函数 ( 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>
Run Code Online (Sandbox Code Playgroud)
有些人可能想知道为什么这会有意义。我的逻辑是,C++ 允许重载const函数参数和成员函数。
const例如,成员函数更喜欢调用const成员重载。
我认为noexcept函数调用noexcept …
如果我std::vector使用默认构造函数(和默认分配器)构造一个空,它可以抛出异常吗?
通常,为容器的元素分配空间可以抛出异常(这将是一个异常std::bad_alloc).但一个默认的构造函数std::vector并不需要分配任何这样的空间.它可以在第一次插入或赋值时懒惰地分配一些空间.但是C++标准是否要求它不会抛出异常(暗示延迟分配,或者捕获std::bad_alloc然后再回到延迟分配)?
我noexcept在 C++14 中发现了一个奇怪的操作符行为。以下代码可以通过 gcc 和 clang(使用 --std=c++14 选项)很好地编译。
// test.cpp
#include <iostream>
#include <type_traits>
#if 1
#define TESTREF(X) X&&
#else
#define TESTREF(X) X const&
#endif
template <class F, class... Args>
struct is_noexcept_callable
: public std::conditional_t<noexcept(std::declval<F>()(std::declval<Args>()...)), std::true_type, std::false_type> {};
template <
class F,
std::enable_if_t<is_noexcept_callable<F,int>::value,int> = 0
>
int evalInt(int x, TESTREF(F) f) noexcept
{
return static_cast<int>(f(x));
}
template <
class F,
std::enable_if_t<!is_noexcept_callable<F,int>::value,int> = 0
>
int evalInt(int x, TESTREF(F) f)
{
return static_cast<int>(f(x));
}
int id(int x) noexcept { …Run Code Online (Sandbox Code Playgroud) 在关于异常规范的 C++ Primer 上noexcept,据说指向可能抛出隐式(没有异常规范定义,例如:)void(*p)();或显式(void(*p)() noexcept(false);)的函数的指针可以指向任何函数,甚至指向不抛出的函数。
另一方面,一个不能抛出异常的函数指针(noexcept例如void(*p) noexcept;)只能指向一个不会抛出异常的函数。
我发现这非常合乎逻辑,因为第一个指针可以从抛出函数指针指向非抛出函数,而第二个指针也很合乎逻辑。
我尝试过这个来了解更多:
void func1(){ // may throw
std::cout << "func1()\n";
}
void func2() noexcept(false){ // may throw
std::cout << "func2()\n";
}
void func3() noexcept(true){ // won't throw
std::cout << "func3()\n";
}
void func4() noexcept{ // won't throw
std::cout << "func4()\n";
}
int main(int argc, char* argv[]){
void(*pFn1)();
pFn1 = func1; // OK
pFn1 = func2; // OK
pFn1 = func3; // OK
pFn1 …Run Code Online (Sandbox Code Playgroud) 考虑以下内容,它使用三元运算符来获取两个 lambda 的公共函数指针类型
int main() {
true ? [](auto) noexcept {} : [](int) {};
}
Run Code Online (Sandbox Code Playgroud)
GCC-trunk 仅在 C++14 中接受它,但在 C++17/20 中拒绝它(演示):
<source>:2:8: error: operands to '?:' have different types 'main()::<lambda(auto:1)>' and 'main()::<lambda(int)>'
2 | true ? [](auto) noexcept {} : [](int) {};
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
Clang-trunk 在所有 C++14/17/20 模式下接受它(演示)。
MSVC-trunk 仅在 C++20 中接受它,但在 C++14/17 中拒绝它(演示):
<source>(2): error C2446: ':': no conversion from 'main::<lambda_01e5bb79b5a210014fb78333f6af80f9>' to 'main::<lambda_57cf6f5767bc1bee4c1e1d9859a585d2>'
<source>(2): note: No user-defined-conversion operator available that can perform this …Run Code Online (Sandbox Code Playgroud) noexcept ×10
c++ ×9
c++11 ×4
bad-alloc ×1
c++14 ×1
c++17 ×1
c++20 ×1
compilation ×1
exception ×1
lambda ×1
overloading ×1
stdvector ×1
tags ×1
unique-ptr ×1
vector ×1