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
, требует ли стандарт, чтобы он оценивался, даже если возможно копирование/перемещение?
Ответы
Ответ 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-неквалифицированным типом, что и тип возврата функции, операцию копирования/перемещения можно опустить, построив автоматический объект непосредственно в возвращаемое значение функции