Стандарт позволяет создавать экземпляры шаблонов функций после вмещающего объявления области пространства имен или в конце единицы перевода, когда на них ссылаются из не шаблонного контекста: [temp.point] / 1
Для специализации шаблона функции, специализации шаблона функции-члена или специализации для функции-члена или статического члена данных шаблона класса, если специализация создается неявно, поскольку на нее ссылаются изнутри другая специализация шаблона и контекст, на который она ссылается, зависят от параметра шаблона, точка создания специализации - это точка создания включающей специализации. В противном случае точка создания такой специализации сразу же следует за объявлением или определением области пространства имен, относящимся к специализации.
[temp.point] / 8
Специализация для шаблона функции, шаблона функции-члена или функции-члена или состояния c член данных шаблона класса может иметь несколько точек создания экземпляров в единице перевода, и в дополнение к описанным выше моментам создания экземпляров для любой такой специализации, которая имеет точку создания экземпляров в пределах единицы перевода, конец единицы перевода также считается точкой создания экземпляров. Специализация для Шаблон класса имеет не более одной точки создания экземпляра в единице перевода. Специализация для любого шаблона может иметь точки реализации в нескольких единицах перевода. Если две разные точки инстанцирования придают шаблону специализации разные значения в соответствии с правилом одного определения, программа некорректна, диагностика не требуется.
Теперь рассмотрим этот минимальный воспроизводимый пример:
#include <iostream>
#include <array>
struct A {};
std::array<char, 2> show(float, A)
{
std::cout << "2\n";
return {};
}
template<typename T>
struct Fun {
decltype(show(0, T{})) b;
};
template <typename T>
void func(T, int c = sizeof(Fun<T>{}.b))
{
show(0, T{});
std::cout << c << '\n';
}
int main()
{
func(A{});
}
char show(int, A)
{
std::cout << "1\n";
return {};
}
И G CC, и выход Clang 1
2
( Godbolt ).
Здесь экземпляр func<A>
(срабатывает в main
) имеет две точки создания: одну сразу после main
(и, следовательно, перед второй show
) и другую в конце единицы перевода. Первый 1
указывает, что компиляторы должны создавать экземпляр func<A>
в конце блока перевода. Однако аргумент по умолчанию sizeof(Fun<T>{}.b)
вызывает создание экземпляра Fun<A>
, а второй 2
предполагает создание экземпляра Fun<A>
перед вторым show
.
Теперь задана точка создания аргументов по умолчанию, равная func<A>
: [temp.point] / 2
Если функция шаблон или функция-член шаблона класса вызывается способом, в котором используется определение аргумента по умолчанию для этого шаблона функции или функции-члена, точка создания аргумента по умолчанию является точкой создания шаблона функции или специализация функции-члена.
Хмм ... Похоже, что эти два числа должны быть одинаковыми.
Мне кажется, что я что-то здесь упускаю. Есть ли какая-то деталь, которой я случайно пренебрегал? Или я ошибся?