Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Point of evaluation of exception specification

Consider these code snippets:

Version (1)

void q() {}
class B {
  void f() noexcept(noexcept(q())) {q(); }
  decltype(&B::f) f2;
};

Version (2)

void q() {}
class B {
  void f() noexcept(true) {q(); }
  decltype(&B::f) f2;
};

Version (3)

void q() {}
class B {
  void f() noexcept {q(); }
  decltype(&B::f) f2;
};

All versions of GCC compile these code snippets without any error or warning (including trunk-version). All versions of Clang which support C++17 deny version (1) and (2), but not version (3), with the following error:

<source>:4:16: error: exception specification is not available until end of class definition

  decltype(&B::f) f2;

               ^

Take into account that the standard defines noexcept as equivalent to noexcept(true) [except.spec]. Thus, version (2) and version(3) should be equivalent, which they are not for clang.

Thus, the following questions: At which point do exception specifications need to be evaluated according to C++17 standards? And, if some codes above are invalid, what is the rational behind?


Abstract background for those who are interested:

template <typename F>
struct result_type;

template<typename R, typename C, typename... Args>
struct result_type<R(C::*)(Args...)> {
  using type = R;
}; // there may be other specializations ...

class B {
  int f() noexcept(false) { return 3; }
  typename result_type<decltype(&B::f)>::type a;
};

This code should be valid at least up to C++ 14, because noexcept was not part of the function type (for clang, it compiled up to version 3.9.1). For C++ 17, there is no way to do this.

like image 841
overseas Avatar asked May 20 '18 11:05

overseas


1 Answers

This is a result of CWG 1330.

Basically, the class is considered to be complete within its noexcept-specifier (in the resolution of the defect above it's referred to as an exception-specification).

This puts us in a situation where:

void q() {}
class B {
  void f() noexcept(noexcept(q())) {q(); }
//                  ~~~~~~~~~~~~~
//                  evaluated in the context of complete B

  decltype(&B::f) f2;
//~~~~~~~~~~~~~~~
//cannot wait until B is complete to evaluate
};

We need to know decltype(&B::f) to process the declaration of B::f2, but in order to know that type, we need to know the what the noexcept-specifier of B::f is (because now that's part of the type system), but in order to do that, we need to evaluate the noexcept-specifier in the context of the complete B.

The program is ill-formed, clang is correct.

like image 111
Barry Avatar answered Sep 17 '22 15:09

Barry