Точка создания аргументов по умолчанию в шаблонной функции - PullRequest
5 голосов
/ 30 марта 2020

Стандарт позволяет создавать экземпляры шаблонов функций после вмещающего объявления области пространства имен или в конце единицы перевода, когда на них ссылаются из не шаблонного контекста: [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

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

Хмм ... Похоже, что эти два числа должны быть одинаковыми.

Мне кажется, что я что-то здесь упускаю. Есть ли какая-то деталь, которой я случайно пренебрегал? Или я ошибся?

1 Ответ

3 голосов
/ 03 апреля 2020

Как указано в вопросе [temp.point] / 8 говорит:

Если две разные точки реализации дают шаблону специализации разные значения в соответствии с одним определением Как правило, программа некорректна, диагностика не требуется c.

Согласно правилу одного определения два определения не совпадают, если разрешение перегрузки вызова функции для имени, используемого в определении приведет к различным объектам, определенным вне определения. ( [basi c .def.odr] /6.2)

Разрешение перегрузки двух вызовов show в func<A> и Fun<A> выберет различные перегрузки функций в зависимости от того, находится ли точка создания func<A> непосредственно после main или в конце единицы перевода, обе из которых являются допустимыми точками создания экземпляров.

Следовательно, программа ill- сформирован, без диагностики c требуется .

...