Reputation: 1316
For the program below, Clang 5 (trunk) reports that IsNoexcept
is not deducible, while GCC 7.1 segfaults. What does the standard (draft) say about this? Is this a compiler QOI issue?
static_assert(__cpp_noexcept_function_type, "requires c++1z");
template<typename T>
struct is_noexcept;
template<bool IsNoexcept>
struct is_noexcept<void() noexcept(IsNoexcept)> {
static constexpr auto value = IsNoexcept;
};
static_assert(is_noexcept<void() noexcept>::value);
static_assert(!is_noexcept<void()>::value);
int main() {}
Relates to proposal P0012.
Upvotes: 9
Views: 859
Reputation: 137425
noexcept
to ease the implementation of std::is_function
. It looks like the extension is only very lightly tested.This is not a conforming extension because it changes the meaning of well-defined code, e.g., the value of g(f)
with the following snippet:
void f() noexcept;
template<bool E = false, class R>
constexpr bool g(R (*)() noexcept(E)){
return E;
}
Upvotes: 6
Reputation: 41145
From what I can tell, the value of the expression contained within the noexcept
specifier does not become part of a function's type.*
N4618 §8.3.5/1 [dcl.fct] states (emphasis mine)
In a declaration
T D
whereD
has the form
D1
( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifieroptattribute-specifier-seqopt
and the type of the contained declarator-id in the declarationT D1
is “derived-declarator-type-listT
”, the type of the declarator-id inD
is “derived-declarator-type-listnoexcept
opt function of (parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt returningT
”, where the optionalnoexcept
is present if and only if the exception specification (15.4) is non-throwing. The optional attribute-specifier-seq appertains to the function type.
So this means that the type of a function either includes noexcept
or not; if the expression expr within noexcept(expr)
evaluates to false
, then the function's type will exclude the keyword noexcept
altogether.
So you're forced to do something like this instead:
template<typename T>
struct is_noexcept
{
static constexpr bool value = false;
};
template<>
struct is_noexcept<void() noexcept> {
static constexpr auto value = true;
};
However, I think that the fact that code like this compiles is misleading:
void (*fp)() noexcept(false);
Because the type of the following function:
void foo() noexcept(false)
{
}
Is void()
.
And the type of the following function:
void bar() noexcept(true)
{
}
is void() noexcept
BUT, we can do this:
void (*fp)() noexcept(false) = &bar;
fp();
Even though bar
is declared noexcept
, we're allowed to assign the function pointer to it! So it's misleading; I can't find a standard reference for it, but it appears that the rules allow such conversions implicitly in order to accommodate an incredible amount of backwards compatibility. §5/14.2 [expr] talks about this wrt to composite pointer types.
Thankfully, though, this is illegal:
void (*fp)() noexcept(true) = &foo;
(See the example at N4618 §4.13 [conv.fctptr] for reference).
*integration of exception specifiers into the type system was proposed in N4320 (which was adopted into the C++17 standard)
Upvotes: 1