Причина, по которой этот компилятор находится здесь, в [temp.point] ¶8 :
Специализация для шаблона функции, шаблон функции-члена,
или функция-член или статический член данных шаблона класса может
иметь несколько точек реализации в пределах единицы перевода, и
в дополнение к описанным выше точкам реализации, для любого
такая специализация, которая имеет точку конкретизации в рамках
блок перевода, конец блока перевода также считается
точка создания экземпляра. Специализация для шаблона класса имеет в
самое большее один момент создания экземпляра в единице перевода [...] Если две разные точки создания дают специализации шаблона разные значения
согласно правилу единого определения, программа плохо сформирована, нет диагностики
требуется.
Обратите внимание на окончание этой цитаты, поскольку мы дойдем до нее в редактировании ниже, но сейчас то, что происходит на практике по фрагменту OP, - это то, что компилятор использует дополнительно рассматриваемый момент создания экземпляра make_unique()
, который помещается в конце блока перевода, чтобы в нем были определения, отсутствующие в исходной точке использования в коде. Это разрешено делать в соответствии с этим пунктом из спецификации.
Обратите внимание, это больше не компилируется:
struct Foo; int main(){ std::make_unique<Foo>(); } struct Foo { ~Foo() = delete; };
Как и в случае, компилятор не пропускает момент создания экземпляра, он только откладывает его в отношении того, какую точку в единице перевода он использует для генерации кода для шаблона.
Редактировать: Наконец, кажется, что, хотя у вас есть эти несколько точек создания экземпляров, это не означает, что поведение определяется, если определение отличается между этими точками. Обратите внимание на последнее предложение в приведенной выше цитате, согласно которому эта разница определяется правилом One Definition . Это взято прямо из моего комментария к ответу @hvd, который обнародовал это здесь:
См. здесь в Правиле Единого Определения :
Каждая программа должна содержать ровно одно определение каждого не встроенного
функция или переменная, которая используется в этой программе за пределами
отклоненное заявление; Диагностика не требуется. Определение может появиться
явно в программе, его можно найти в стандарте или
пользовательская библиотека или ...
И поэтому в случае ОП очевидно, что есть разница между двумя точками реализации, поскольку, как отметил сам @hvd, первый имеет неполный тип, а второй - нет. На самом деле, это различие составляет два разных определения, и поэтому очень мало сомнений в том, что эта программа плохо сформирована.