#c #compiler-errors #language-lawyer
#c #ошибки компилятора #язык-юрист
Вопрос:
По-видимому, существует разница в том, когда шаблоны расширяются для членов класса, у которых есть инициализаторы по умолчанию между MSVC и Clang, что иногда может привести к тому, что код, который успешно компилируется в MSVC, но терпит неудачу в Clang.
Рассматриваемый проблемный код был довольно сложным и распределен по нескольким файлам, но я думаю, что следующий игрушечный пример показывает то же несоответствие:
#include <memory>
class Impl;
class A {
std::unique_ptr<Impl> ptr = nullptr;
public:
A();
~A();
};
int main() {}
Как видно из проводника компилятора, Clang выдает ошибку для этого кода. Если = nullptr
удалить, оба компилятора будут работать без ошибок.
Очевидно, что этот код ничего не сделает, и даже если бы он это сделал, = nullptr
он все равно не понадобился бы. Однако мне любопытно, есть ли в стандарте что-нибудь, что говорит о том, является ли один или другой из этих компиляторов правильным в этом случае?
Комментарии:
1.Я бы предположил, что это как-то связано с инициализацией (семантически), использующей временный, который нуждается в очистке, который вызывает deleter, который является единственной частью
unique_ptr
, которая требуетImpl
полного типа, но то же самое происходит в более новых версиях C , где нет временного (посмотрите на ASTS)… однако gcc соглашается с жалобами clang: godbolt.org/z/vdVdb92. Та же ошибка с демонстрацией gcc, но для меня msvc кажется правильным (даже если оба gcc / clang согласны).
3. Мои два цента. выполняется инициализация копирования . В нем говорится, что » соответствующий конструктор (перемещение или копирование) должен быть доступен, даже если он не используется. (до C 17) » Следовательно, в этом случае для копирования / перемещения экземпляра шаблона потребуется деструктор (потому что так
std::unique_ptr
работает). Однако я не могу дать ответ, потому-std=c 17
что выдает ту же ошибку (она не должна)4. @BiagioFesta: Код не генерируется, у нас просто есть
A
определение. Никакие методы не определены, просто объявлены (даже неявно) (игнорирование<memory>
🙂 ).5. класс
Impl
должен быть полным типом. Я удивлен, что MSVC этого не уловил.
Ответ №1:
Здесь есть несколько ошибок, см. https://bugs.llvm.org/show_bug.cgi?id=39363#c8
Итак: две ошибки в GCC (хотя это может быть одно и то же), одна ошибка в поддержке Clang C 17, одна ошибка в libstdc и никаких ошибок в libc . Перенаправление на это как на ошибку Clang. 🙂
И libstdc на самом деле был дефектом в стандарте, который был непреднамеренно исправлен https://wg21.link/lwg2081 (см . https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87704 для получения более подробной информации).
Я думаю, что программа на самом деле недействительна в режиме C 14, потому что инициализатор элемента по умолчанию использует инициализацию копирования, поэтому создается временное, а затем перемещается, а затем временное уничтожается. Уничтожение временного означает, что должен быть создан экземпляр деструктора, для чего требуется полный тип. В режиме C 17 гарантированное удаление копии означает, что нет временного и, следовательно, нет создания экземпляра деструктора, и код должен быть действительным. Но GCC и Clang оба делают неправильные вещи, даже в режиме C 17.
Если вы используете прямую инициализацию списка вместо инициализации копирования, то это работает с Clang:
std::unique_ptr<Impl> ptr{nullptr};
И это тоже работает:
std::unique_ptr<Impl> ptr{};
И эквивалент:
std::unique_ptr<Impl> ptr = {};
И просто не предоставление инициализатора вообще также работает, и работает с GCC тоже:
std::unique_ptr<Impl> ptr;
Комментарии:
1. Комментарий Ричарда Смита объясняет рассуждения более подробно, включая концепцию «потенциально вызываемого», которая здесь уместна.
2. » потому что инициализатор элемента по умолчанию использует инициализацию копирования «. Ну, я подумал то же самое. Однако место, где все должно произойти
A::A()
, определено в другом TU. Итак, действительно ли нам нужно ожидать ошибки в определении класса (даже если конструктор будет определен в другом TU)?3. Есть ошибки компилятора, поэтому происходит не то, что вы ожидаете.
4. Я не уверен, что понимаю, почему эта программа будет недействительной в режиме C 14, потому что, как упоминалось выше, я думал, что инициализация копирования будет происходить только в определении конструктора, которого здесь не существует. Я отмечаю это как ответ, потому что сообщения об ошибках действительно объясняют различия компилятора, о которых я спрашивал.