Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defaulted move assignment cannot be explicitly noexcept if a member has a non-trivial noexcept assignment operator

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.

like image 493
pmr Avatar asked Dec 16 '13 12:12

pmr


1 Answers

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 as constexpr, 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, or

  • both 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 if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(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.

like image 107
Howard Hinnant Avatar answered Oct 16 '22 18:10

Howard Hinnant