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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With