Уступка по умолчанию не может быть явно не исключена, если член имеет нетривиальный оператор присваивания исключений

Этот код не скомпилирован с gcc 4.8.2 (-std = С++ 11), но компилируется с clang 3.4 (trunk) (-std = С++ 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;
}

static_assert - интересная часть gcc: присваивание по умолчанию будет noexcept, но я не могу объявить его таким образом.

Вариант, не включающий vector:

template<bool b>
struct F {
  F& operator=(F&&) noexcept(b) {return *this;}
};

struct X {
  X& operator=(X&&) noexcept = default;
  F<true> f;
};

Каково ожидаемое поведение здесь? Интуитивно clang кажется правильным.

Ответы

Ответ 1

Пример, не содержащий vector, должен компилироваться. clang является правильным на этом.

Пример, включающий vector, может компилироваться или не компилироваться, в зависимости от того, помечен ли поставщик std:: lib оператору присваивания перемещения vector как noexcept или нет. Стандарт не требует, чтобы эта подпись была noexcept. Стандарт позволяет поставщикам добавлять noexcept, если функция никогда не будет бросать.

libС++ помещает оператор присваивания vector как noexcept, если allocator_traits<allocator_type>::propagate_on_container_move_assignment::value - true, а is_nothrow_move_assignable<allocator_type>::value - true (как соответствие расширение). Оба они верны в реализации libС++ для std::allocator<T>.

Обновление

Вытащить шляпу адвоката языка по запросу.

<disclaimer>

Я сощурился в половине стандарта, который не является моей областью знаний.

</disclaimer>

Не могли бы вы более подробно объяснить, почему должен компилироваться пример?

Все цитаты из последнего рабочего проекта С++ 1y, N3797.


Это указывает, что спецификация исключения разрешена для явно заданного специального члена:

8.4.2 Явно-дефолтные функции [dcl.fct.def.default]/p2

2 Явно-дефолтная функция может быть объявлена ​​ constexpr только если он был бы неявно объявлен как constexpr и может иметь явная спецификация исключений, только если она совместима (15.4) с спецификацией исключения для неявного объявления.


Это определяет "совместимые спецификации исключений:"

15.4 Спецификации исключений [except.spec]/p3

3 Две спецификации исключения совместимы, если:

  • оба не бросают (см. ниже), независимо от их формы,

  • оба имеют форму noexcept (константное выражение), а константные выражения эквивалентны, или

  • оба являются спецификациями динамического исключения, которые имеют одинаковый набор настроенных типов.

Bullet 2 охватывает ваш случай.


Это объясняет, почему неявно объявленный специальный член noexcept в вашем примере:

15.4 Спецификации исключений [except.spec]/p14

14 Наследующий конструктор (12.9) и неявно объявленный специальный Функция члена (раздел 12) имеет спецификацию исключения. Если f наследующий конструктор или неявно объявленный дефолт конструктор, конструктор копирования, конструктор перемещения, деструктор, копия оператор присваивания или оператор переадресации, его неявный exception-specification указывает идентификатор типа T тогда и только тогда, когда T допускается исключительно спецификацией исключения функции вызывается с помощью неявного определения f; f разрешает все исключения, если таковые имеются функция, которую он вызывает напрямую, разрешает все исключения, а f имеет exception-specification noexcept(true), если каждая функция, которую он вызывает напрямую, не допускает исключений.

Поскольку каждая функция, вызванная неявным объявлением X& operator=(X&&), не допускает исключений (т.е. оператора присваивания перемещения F<true>), этот специальный элемент noexcept(true).


Я считаю, что это гвоздь.