Новые компиляции неполного типа, когда они завернуты в шаблон

Рассмотрим этот код с очевидной ошибкой компиляции: (1)

struct A;
struct B {
  B() { new A(); } // error: allocation of incomplete type 'A'
};

Использование unique_ptr не поможет: (2)

struct A;
struct B {
  B() { std::make_unique<A>(); } // error: due to ~unique_ptr()
};

Тогда (к моему большому удивлению) я узнал, что этот будет компилировать: (3)

struct A;
struct B {
  B() { std::make_unique<A>(); }
};
struct A {}; // OK, when a definition is added **below**

Затем я проверил, помогает ли это с new - nope: (4)

struct A;
struct B {
  B() { new A(); } // error: allocation of incomplete type 'A'
};
struct A {};

Я полагал, что это имеет какое-то отношение к template и на самом деле: wrapping new внутри template делает компиляцию: (5)

template <typename T> 
T* my_new() { return new T(); } // OK, when wrapped in template
struct A;
struct B {
  B() { my_new<A>(); }
};
struct A {};

И только ради полноты, удаление определения A снова вызывает ошибку: (6)

template <typename T> 
T* my_new() { return new T(); } // error: allocation of incomplete type 'A'
struct A;
struct B {
  B() { my_new<A>(); }
}; 
// do note: definition of A removed

Что здесь происходит? Насколько я понял, компилятор должен знать размер/определение A, чтобы выделить его, просто объявив его, недостаточно. Кроме того, я полагал, что определение должно предшествовать распределению.

Это кажется правильным, если непосредственно использовать new (1,4). Но когда new завернуто, очевидно, что я ошибаюсь (2,3,5,6).

Возможные объяснения, которые я нашел до сих пор:

  • Проверка завершенных типов задерживается до тех пор, пока не произойдет создание template. Я думаю, что это правильно, но в моем случае прямое использование new A() и вызов my_new<A>() происходят практически в одной и той же позиции. Так что это не может быть причиной. Правильно?
  • Использование неполных типов в качестве template Аргументы могут быть undefined. Это правда? Даже при включении всех предупреждений компилятор не будет жаловаться. Кроме того, сравнение 5 и 6, по-видимому, предполагает, что компилятор достаточно умен, чтобы понять, что определение ниже (таким образом, фактически завершая тип).

Почему 4 считается некорректным, в то время как 5 компиляций (или 5 просто ложно компилируют поведение undefined [но затем 3 также должны быть ошибочными, правильно?])?

btw: проверено с помощью clang++ - 3.5.0 и g++ - 4.9.2

Ответ 1

§14.6.4.1 [temp.point]/p1,8, акцент мой:

1 Для специализации шаблона функции шаблон функции-члена специализации или специализации для функции-члена или статической член данных шаблона класса, если специализация неявно экземпляр, поскольку он ссылается из другого шаблона специализация и контекст, с которой он ссылается, зависят от параметр шаблона, момент создания специализации является точкой инстанцирования охватывающей специализации. В противном случае точка инстанцирования для такой специализации сразу следует за объявлением или определением области пространства имен относится к специализации.

8 Специализация шаблона функции, шаблон функции-члена, или функции-члена или статического элемента данных шаблона класса могут имеют несколько точек инстанцирования внутри единицы перевода и в дополнение к моментам, описанным выше, для любого такая специализация, которая имеет точку инстанцирования в пределах единицы перевода, конец единицы перевода также считается точка инстанцирования. Специализация шаблона класса имеет почти одна точка инстанцирования внутри единицы перевода. специализация для любого шаблона может иметь точки инстанцирования в множественные единицы перевода. Если две разные точки инстанцирования дать типовую специализацию различных значений в соответствии с одной (3.2), программа плохо сформирована, отсутствует диагностика требуется.

Существуют две точки инстанцирования my_new<A>, одна в конце определения B и одна в конце единицы перевода. Поскольку эти две точки приведут к различным значениям (для фрагментов 3 и 5), программа является плохо сформированным NDR (то есть имеет поведение undefined).