Разбор последствий: почему тип шаблонного параметра, встречающийся только в возвращаемом типе функции, не выводится из контекста присваивания?
Тип шаблонного параметра не выводится из возвращаемого типа функции: при выводе аргументов шаблона компилятор анализирует прежде всего фактические аргументы вызова, а тип переменной слева от присваивания не используется. Поэтому вызов функции, у которой параметр шаблона встречается только в возвращаемом типе, требует явного указания шаблонного аргумента или изменения интерфейса функции.
В первом вызове T задан явно. Во втором у функции нет обычных параметров, из которых можно было бы вывести T, а тип auto слева не участвует в выводе аргументов шаблона.
Механизм вывода аргументов шаблона проектировался вокруг информации, доступной в месте вызова: типов и категорий фактических аргументов функции. Это позволяет выбрать специализацию или перегрузку до выполнения тела функции и без зависимости от предполагаемого типа результата.
Возвратный тип намеренно не является обычным источником вывода. Если бы компилятор регулярно выводил параметры из контекста присваивания, выбор перегрузки мог бы зависеть от внешнего выражения и усложнять разрешение вызовов, особенно при наличии нескольких подходящих функций.
Рассмотрим фабричную функцию, которая должна вернуть объект неизвестного на этапе написания вызова типа. Интуитивно кажется, что при присваивании в контейнер типа std::vector<int> компилятор мог бы получить int из ожидаемого результата.
В обычном вызове функции это не происходит: сначала требуется определить шаблонные аргументы и саму функцию, а уже затем анализируется возвращаемое значение и его совместимость с контекстом. Неверное предположение о выводе из присваивания приводит к ошибке компиляции, а не к автоматическому выбору типа.
При выводе аргументов шаблона компилятор сопоставляет параметры функции с фактическими аргументами вызова. Если T встречается только в возвращаемом типе, это вхождение не дает выводимой информации, поэтому T остается неизвестным.
Тип слева от присваивания, результат контекстного преобразования и ожидаемый тип выражения обычно не участвуют в выводе параметров функции. Ключевое слово auto не меняет правило: сначала должен быть корректно сформирован вызов функции, и только после этого auto выводится из его результата.
Есть три типичных способа решить задачу:
int;Явное указание типа проще и обычно наиболее прозрачно для фабрик. Дополнительный параметр улучшает вывод, но может ухудшить читаемость API, особенно если он нужен только для компилятора. Автоматический вывод возвращаемого типа из тела функции в C++14 и позднее — другой механизм: он выводит тип реализации функции, но не позволяет вывести внешний параметр шаблона, который нигде не задан.
Если T встречается одновременно в параметрах функции и в возвращаемом типе, он может быть выведен из параметров, а затем использован для формирования результата. Проблема возникает именно тогда, когда все сведения о T находятся только в возвращаемом типе.
В библиотеке есть обобщенная функция чтения значения из конфигурации, возвращающая T. Разработчик пытается написать присваивание результата в переменную конкретного типа и ожидает, что тип автоматически передастся функции через контекст назначения.
Рассматривались варианты:
Выбрано явное указание типа: для операции чтения тип результата является существенной частью запроса, поэтому его явная фиксация делает код предсказуемым. В результате вызовы не зависят от контекста присваивания и корректно работают даже внутри более сложных выражений.
1. Вопрос: Может ли decltype в возвращаемом типе заставить компилятор вывести параметр шаблона?
Ответ: Нет. decltype определяет форму возвращаемого типа после того, как шаблонные параметры уже известны, но сам по себе не создает источник для их вывода. Если T больше нигде не встречается в выводимой позиции, вызов все равно потребует явного аргумента шаблона.
2. Вопрос: Что изменится, если добавить параметр функции, но не передавать его явно?
Ответ: Ничего, если этот параметр не дает фактической информации для сопоставления. Например, параметр, зависящий от T, помогает только тогда, когда в вызове присутствует соответствующий аргумент, из которого можно получить T. Один лишь факт наличия параметра в объявлении не делает тип выводимым.
3. Вопрос: Почему шаблонная функция с параметром в возвращаемом типе не может выбрать T по типу присваивания, хотя перегрузки иногда различаются возвращаемыми типами?
Ответ: Обычная перегрузка функции не может различаться только возвращаемым типом: такой тип не участвует в выборе перегрузки, поскольку вызов должен быть определен до проверки результата. Шаблонный вывод подчиняется той же общей логике: возвращаемый тип формируется после определения шаблонных аргументов и не используется как источник вывода при обычном вызове.