Подтвердить что ты не робот

Clang и GCC против MSVC и ICC: Требуется ли static_assert в конструкторе copy/move, если это может быть применимо при копировании/перемещении?

У меня есть static_assert в конструкторе перемещения структуры шаблона. Является ли этот static_assert обязательным для рассмотрения компилятором, даже если возможно копирование?

Это урезанный сценарий:

#include <type_traits>

template<typename T>
struct X
{
  X(X&&) { static_assert(std::is_same<void, T>::value, "Intentional Failure"); }
};

auto impl() -> X<int>;    
auto test() -> decltype(impl())
{
  return impl();
}

int main()
{
  test();
}

GCC и Clang соглашаются оценить static_assert и не скомпилировать.
MSCV и ICC, с другой стороны, компилируют код очень хорошо.

Интересно, когда я удаляю определение конструктора move и просто объявляю его следующим образом:

template<typename T>
struct X
{
  X(X&&);
};

GCC и Clang также компилируют код сейчас. Таким образом, все компиляторы, похоже, согласны с тем, что определение конструктора перемещения не имеет отношения к копированию.

Вопрос:
Если в конструкторе copy/move существует static_assert, требует ли стандарт, чтобы он оценивался, даже если возможно копирование/перемещение?

4b9b3361

Ответ 1

Ниже следует помощь.

Вам не нужно использовать вывод типа, чтобы проиллюстрировать проблему. Даже более простой пример имеет такую ​​же проблему:

#include <type_traits>

template <typename T>
struct X
{
  X() {}
  X(X&&) { static_assert(std::is_same<void, T>::value, "failed"); }
};

int main()
{
  X<int> x = X<int>();
}

Clang и GCC не будут их компилировать. MSVC компилирует и выполняет штраф.

Это показывает, что проблема связана с odr-use и когда описываются определения функций-членов.

14.7.1 [temp.inst] абзац 2 говорит: "[...] специализация члена неявно создается, когда специализация ссылается в контексте, требующем определения члена"

3.2 [basic.def.odr] пункт 3 говорит (в примечании) "[...] Конструктор, выбранный для копирования или перемещения объекта типа класса, используется как odr, даже если вызов фактически устраняется реализацией "

3.2 [basic.def.odr] пункт 4 говорит: "Каждая программа должна содержать ровно одно определение каждой не-встроенной функции или переменной, которая является odr-используемой в этой программе, не требуется диагностика."

Эрго: специализация должна быть создана, и это утверждение было запущено.

Ответ 2

Я считаю, что ответ отрицательный. Моя логика выглядит так:

  • Копирование elision требует объявления конструкторов copy/move, но не требует определения.
  • Определения функций членов шаблонов не создаются, если их определения не требуются.
  • Если определение не создается, оно не может быть протестировано для того, чтобы быть плохо сформированным.

Литература:

14.7.1.1... Неявное создание экземпляра специализации шаблона вызывает неявное создание объявлений, но не определение, аргументы по умолчанию или спецификации исключений функций-членов класса...

14.7.1.2 Если член шаблона класса... не был явно создан или явно специализирован, специализация элемента неявно создается, когда специализация ссылается в контексте, который требует определения члена существовать...

Ответ 3

Перемещение конструкторов не вызывается. static_assert оценивается после создания X<int>::X(X&&). Скорее всего, некоторые компиляторы оценивают методы шаблонов при использовании (когда вы используете конструктор перемещения, и вы его не используете), а другие - при создании шаблона класса (при первом использовании X<int>).

Ответ 4

Я думаю, что ответ: да.

сначала static_assert заставляет конструктор от "определения" до объявления.

Я не уверен точно о характере шаблона static_assert в отношении раздела 12.8 ниже либо...

(Извиняюсь за форматирование...)

с © ISO/IEC N3242 = 11-0012 7 Объявления [dcl.dcl]

2. Объявление является определением, если оно не объявляет функцию без указания тела функций (8.4), содержит спецификатор extern (7.1.1) или спецификацию 25 (7.5) привязки и ни инициализатор, ни тело функции, оно объявляет статический член данных в определении класса (9.4), это объявление имени класса (9.1), это непрозрачная-enum-декларация (7.2), или это объявление typedef (7.1.3), использование-декларация (7.3.3), static_assert-декларация (раздел 7), объявление атрибута (раздел 7), декларация с пустым (раздел 7) или директива using (7.3.4)

12.8 Копирование и перемещение объектов класса [class.copy]

7 Шаблон функции-члена никогда не создается для выполнения копии объекта класса для объекта его типа класса. [Пример:

struct S {
template<typename T> S(T);
template<typename T> S(T&&);
S();
};

S f();
const S g;

void h() {
S a( f() );// does not instantiate member template;
// uses the implicitly generated move constructor


S b(g);// does not instantiate the member template;
// uses the implicitly generated copy constructor
}

- конец примера ]

32 Когда определенные критерии выполнены, реализации разрешено опускать конструкцию copy/move объекта класса, даже если конструктор copy/move и/или деструктор для объекта имеют побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования/перемещения как просто два разных способа обращения к одному и тому же объекту, а уничтожение этого объекта происходит в более поздние времена, когда эти два объекта были бы разрушен без оптимизации.   123 - Это разрешение операций копирования/перемещения, называемое копированием, разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий):   - в операторе return в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause) с тем же CV-неквалифицированным типом, что и тип возврата функции, операцию копирования/перемещения можно опустить, построив автоматический объект непосредственно в возвращаемое значение функции