I claim that this program ought to be well-formed: it declares a constexpr member function of S<int>
. However, both GCC and Clang reject this program.
template<class T>
struct S {
constexpr int foo() {
if constexpr (std::is_same_v<T, int>) {
return 0;
} else {
try {} catch (...) {}
return 1;
}
}
};
int main()
{
S<int> s;
return s.foo(); // expect "return 0"
}
GCC says:
error: 'try' in 'constexpr' function
Clang says:
error: statement not allowed in constexpr function
Neither of them seem to notice that the "try" statement is located in a discarded branch of the if constexpr
statement.
If I factor the try
/catch
out into a non-constexpr member function void trycatch()
, then both Clang and GCC are happy with the code again, even though its behavior ought to be equivalent to the unhappy version.
template<class T>
struct S {
void trycatch() {
try {} catch (...) {}
}
constexpr int foo() {
if constexpr (std::is_same_v<T, int>) {
return 0;
} else {
trycatch(); // This is fine.
return 1;
}
}
};
Is this
- a bug in both GCC and Clang?
- a defect in the Standard, which GCC and Clang are faithfully implementing?
- a Quality of Implementation issue due to the "conditional constexprness" of
foo()
?
(Irrelevant background: I'm implementing constexpr any::emplace<T>()
for an allocator-aware version of any
whose allocator might be constexpr-per-P0639 (i.e. it might lack a deallocate
member function) or might not. In the former case we don't want or need the try
; in the latter case we need the try
in order to call deallocate
if the constructor of T
throws.)