Предскажите результат компиляции: может ли компилятор вывести T из значения аргумента функции по умолчанию?...

Предскажите результат компиляции: может ли компилятор вывести T из значения аргумента функции по умолчанию?

#include <iostream>

template<class T>
void emit(T value = T{}) {
    std::cout << sizeof(T);
}

int main() {
    emit();
}
Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Код не скомпилируется: при вызове emit() шаблонный параметр T невозможно вывести. Значение аргумента функции по умолчанию используется только после вывода шаблонных параметров и само по себе в выводе типов не участвует.

Исторический контекст

Вывод шаблонных параметров в C++ основан на сопоставлении фактически переданных аргументов с параметрами шаблонной функции. Значения аргументов по умолчанию позволяют не передавать параметр функции, но не создают дополнительный источник информации для вывода типа.

Такое разделение важно: компилятор сначала должен получить конкретную специализацию шаблона, а уже затем определить, какие значения аргументов использовать, включая значения по умолчанию.

Постановка проблемы

В объявлении emit выражение T{} действительно зависит от T, но оно находится внутри значения по умолчанию. При вызове без аргументов нет фактического выражения, из которого можно было бы вывести тип.

Следовательно, компилятор не может выбрать специализацию вроде emit<int> или emit<double>. Ошибка возникает до формирования корректного значения аргумента по умолчанию.

Подробное решение

Вывод типов выполняется по переданным аргументам функции. В вызове emit() список фактических аргументов пуст, поэтому для T нет выводимого контекста.

Запись T value = T{} не означает, что T будет выведен из T{}. Это default argument функции, а не аргумент шаблонного вывода. Кроме того, T{} нельзя сформировать до того, как T уже известен.

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

#include <iostream> template<class T> void emit(T value = T{}) { std::cout << sizeof(T); } int main() { emit<int>(); }

Здесь T равен int, поэтому компилятор может сформировать T{} и вызвать функцию. Другой вариант — задать аргумент шаблона по умолчанию: template<class T = int>. Это уже не вывод типа, а использование default template argument.

Если передать значение явно, вывод выполняется обычным образом: emit(42) выводит T как int, после чего default argument не используется. Практическое правило: значение функции по умолчанию может заменить отсутствующий вызовной аргумент, но не может заменить отсутствующий шаблонный тип.

Ситуация из практики

В обобщённом логгере разработчик хочет разрешить вызов log<Тип>() без явного значения и пишет функцию с параметром T value = T{}. Вызов log() не работает, потому что тип сообщения неизвестен.

Можно потребовать явный тип — log<Сообщение>(). Это однозначно и не усложняет интерфейс, но увеличивает объём вызова. Можно задать T по умолчанию, однако тогда интерфейс всегда будет иметь заранее выбранный тип, что подходит только при действительно осмысленном типе по умолчанию.

На практике выбирают явный шаблонный аргумент для API, где тип семантически важен, и default template argument — только когда тип по умолчанию не создаёт неоднозначности. Попытка вывести тип из T{} не является рабочим вариантом.

Что кандидаты часто упускают

1. Сработает ли вызов emit({})?

Нет. Пустой braced-init-list обычно не предоставляет тип, из которого можно вывести T. Кроме того, это отдельный случай от default argument: здесь аргумент формально передан, но его форма не содержит самостоятельного выводимого типа.

2. Что изменится при объявлении template<class T = int>?

Вызов emit() станет корректным, потому что при отсутствии результата вывода будет использовано значение шаблонного параметра по умолчанию int. Это не означает, что T вывели из значения T{}: тип был взят из default template argument.

3. Будет ли использован default argument функции при вызове emit<double>()?

Да. Явное указание double фиксирует T, после чего функция может создать значение T{} и использовать его как аргумент. Вызов emit<double>(3.5) также корректен, но в этом случае default argument уже не используется.