noexcept 运算符在调用指向成员函数的指针后失败

noexcept operator fails after calling pointer-to-member function

这个 MWE 可能看起来做作,但失败的 static_assert 仍然令人惊讶:

#include <utility>

struct C {
  void f() noexcept { }
  using F = void(C::*)();

  static constexpr F handler() noexcept {
    return &C::f;
  }

  void g() noexcept(noexcept((this->*handler())())) {
  }
};

int main() {
  static_assert(noexcept(std::declval<C>().g()));
}

魔杖盒link:https://wandbox.org/permlink/a8HSyfuyX1buGrbZ

我希望这适用于 Clang 但不适用于 GCC,因为它们在运算符 noexcept 的上下文中对 "this" 的处理方式不同。

鉴于您的 static_assert 没有字符串参数,您使用的是 C++17。在 C++17 中,noexcept 成为类型系统的一部分。这意味着给定:

using F = void(C::*)();

这个 PMF 不是 noexcept。调用它相当于调用一个noexcept(false)成员函数。您需要将函数类型标记为 noexcept:

using F = void(C::*)() noexcept;

该更改允许您的代码编译:

#include <utility>

struct C {
  void f() noexcept { }
  using F = void(C::*)() noexcept;

  static constexpr F handler() noexcept {
    return &C::f;
  }

  void g() noexcept(noexcept((this->*handler())())) {
  }
};

int main() {
  static_assert(noexcept(std::declval<C>().g()));
}

On Godbolt

fnoexcept,但指向它的指针不是。所以在 g 的定义中,this->*handler() return 是一个 PMF,它不是 noexcept(即使你碰巧 return 是 noexcept,所以当你通过写 (this->*handler())() 调用它时,你正在调用一个不是 noexcept 的函数,所以 noexcept 子句有 returns false.

noexcept 添加到第 5 行的末尾即可。