This code fails to compile with gcc 4.8.2 (-std=c++11) but compiles with clang 3.4 (trunk) (-std=c++11):
#include <type_traits>
#include <vector>
struct X {
X& operator=(X&&) noexcept = default;
// adding noexcept this leads to an error in gcc, but works in clang:
// function ‘X& X::operator=(X&&)’ defaulted on its first
// declaration with an exception-specification that differs from the
// implicit declaration ‘X& X::operator=(X&&)’
std::vector<char> m;
};
// this assert holds, even without noexcept
static_assert(std::is_nothrow_move_assignable<X>::value,
"type-specification violation");
int main()
{
return 0;
}
The static_assert
is the interesting part on gcc: the defaulted move assignment will be noexcept
, but I cannot declare it that way.
A variant not involving vector
is:
template<bool b>
struct F {
F& operator=(F&&) noexcept(b) {return *this;}
};
struct X {
X& operator=(X&&) noexcept = default;
F<true> f;
};
What is the expected behavior here? Intuitively clang seems correct.
The example not involving vector
should compile. clang is correct on this one.
The example involving vector
may or may not compile, depending on whether the std::lib vendor has marked the move assignment operator of vector
as noexcept
or not. The standard does not require this signature to be noexcept
. The standard does allow vendors to add noexcept
if the function will never throw.
libc++ marks the vector
move assignment operator as noexcept
if allocator_traits<allocator_type>::propagate_on_container_move_assignment::value
is true and is_nothrow_move_assignable<allocator_type>::value
is true (as a conforming extension). Both of these are true in the libc++ implementation for std::allocator<T>
.
Update
Pulling out the language lawyer hat on request.
<disclaimer>
I'm poking around in the half of the standard that is not my area of expertise.
</disclaimer>
Could you be more explicit on why the example should compile?
All quotes are from the latest C++1y working draft, N3797.
This specifies that an exception-specification is allowed on an explicitly defaulted special member:
8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]/p2
2 An explicitly-defaulted function may be declared
constexpr
only if it would have been implicitly declared asconstexpr
, and may have an explicit exception-specification only if it is compatible (15.4) with the exception-specification on the implicit declaration.
This defines "compatible exception-specifications:"
15.4 Exception specifications [except.spec]/p3
3 Two exception-specifications are compatible if:
both are non-throwing (see below), regardless of their form,
both have the form
noexcept
(constant-expression) and the constant-expressions are equivalent, orboth are dynamic-exception-specifications that have the same set of adjusted types.
Bullet 2 covers your case.
This explains why the implicitly declared special member is noexcept
in your example:
15.4 Exception specifications [except.spec]/p14
14 An inheriting constructor (12.9) and an implicitly declared special member function (Clause 12) have an exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id
T
if and only ifT
is allowed by the exception-specification of a function directly invoked byf
’s implicit definition;f
allows all exceptions if any function it directly invokes allows all exceptions, andf
has the exception-specificationnoexcept(true)
if every function it directly invokes allows no exceptions.
Because every function invoked by an implicitly declared X& operator=(X&&)
allows no exceptions (i.e. the move assignment operator of F<true>
), this special member is noexcept(true)
.
I believe that nails it down.
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