Нетривиальный деструктор делает класс нетривиально-конструктивным

Рассмотрим следующий код:

#include <type_traits>

struct T {};

static_assert(std::is_trivially_destructible< T >{});
static_assert(std::is_trivially_default_constructible< T >{});

struct N { ~N() { ; } };

static_assert(!std::is_trivially_destructible< N >{});
static_assert(!std::is_trivially_default_constructible< N >{});

Он компилируется с использованием clang 3.7.0: живой пример. Но суммируя Стандарт:

Конструктор по умолчанию для класса T является тривиальным (т.е. не выполняет никаких действий), если выполняется все следующее:

  • Конструктор не предоставляется пользователем (т.е. неявно определен или дефолт)
  • T не имеет виртуальных функций-членов
  • T не имеет виртуальных базовых классов
  • T не имеет нестатических элементов с инициализаторами по умолчанию. (начиная с С++ 11)
  • Каждая прямая база T имеет тривиальный конструктор по умолчанию
  • Каждый нестатический член типа класса имеет тривиальный конструктор по умолчанию

Как я вижу, нет никакой зависимости от тривиальности деструктора.

Я что-то пропустил? Это ошибка clang?

ДОПОЛНИТЕЛЬНОЕ

Я нашел обходное решение: static_assert(__has_trivial_constructor( N )); встроенный тип. Существует поддержка в clang, gcc и MSVC.

Для is_noexcept_constructible семейства признаков типа также есть обход.

Ответы

Ответ 1

Эта проблема рассматривается в LWG issue 2116: std:: swap noexcept (что?), мы можем видеть это из раздела cppreference для std:: is_trivially_default_constructible:

Во многих реализациях is_nothrow_default_constructible также проверяет, выбрал ли деструктор, потому что он эффективно не отменен (T()): ошибка GCC 51452 LWG issue 2116

который обманчиво говорит только о is_nothrow_default_constructible, но если мы подробно рассмотрим проблему, мы увидим, что она также применима и здесь.

Возможно, легче, если мы следуем отчет об ошибке gcc: [DR 2116] has_nothrow _. * ошибки конструктора, на которые ссылается первая, в которой говорится:

Признаки, которые обнаруживают конструктивность конструктора nothrow, являются ошибочными, поскольку на них влияет то, имеет ли объект nothrow dtor; разрушение вызывается в конце оценки полного выражения в noexcept (...). Все они используют шаблон построения временного внутри noexcept, тогда как они должны использовать размещение new

это явно говорит то, что на самом деле упоминается только в проблеме LWG, которая в конце концов говорит:

is_nothrow_constructible определяется в терминах is_constructible, который определяется путем просмотра гипотетической переменной и спрашивает, известно ли определение переменной, чтобы не генерировать исключения. Проблема утверждает, что это также рассматривает деструктор типа, учитывая контекст, и таким образом возвращает false, если деструктор может потенциально бросить. По крайней мере, одна реализация (Howard's) возвращает false, если конструктор не является исключением (true), а деструктор - noexcept (false). Так что не напряженная интерпретация. Проблема заключается в том, что это нужно определить с точки зрения размещения new, а не с точки зрения временного объекта, чтобы сделать его более понятным, что is_nothrow_constructible смотрит на noexcept статус только конструктора, а не деструктора.

который также влияет на std::is_trivially_default_constructible, который полагается std:: is_trivially_constructible, который делает то же самое, что и is_constructible, но имеет дополнительное ограничение:

но определение переменной не вызывает никакой операции, которая не является тривиальной