Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

noexcept operator fails after calling pointer-to-member function

This MWE may appear contrived, but the failing static_assert is surprising nonetheless:

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

Wandbox link: https://wandbox.org/permlink/a8HSyfuyX1buGrbZ

I would expect this to work on Clang but not GCC due to their different treatments of "this" in the context of operator noexcept.

like image 760
Jackie Avatar asked Dec 23 '22 13:12

Jackie


2 Answers

Seeing as your static_assert has no string argument, you are using C++17. In C++17, noexcept became part of the type system. What this means is that given:

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

This PMF is not noexcept. Calling it is equivalent to calling a noexcept(false) member function. You need to mark the function type as noexcept:

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

That change allows your code to compile:

#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

like image 85
Justin Avatar answered Jan 24 '23 09:01

Justin


f is noexcept, but the pointer to it is not. So in the definition of g, this->*handler() returns a PMF which is not noexcept (even though you happen to return the address of a MF that is noexcept, so when you call that by writing (this->*handler())() then you are calling a function which is not noexcept, so the noexcept clause there returns false.

Add noexcept to the end of line 5 and it works.

like image 35
Zachary Turner Avatar answered Jan 24 '23 07:01

Zachary Turner