Почему компилятор не может вывести тип шаблона из аргументов по умолчанию?

Я был удивлен, что следующий код привел к тому, что could not deduce template argument for T ошибки could not deduce template argument for T :

 struct foo { template  void bar(int a, T b = 0.0f) { } }; int main() { foo a; a.bar(5); return 0; } 

Вызов a.bar(5) устраняет проблему. Почему компилятор не может вывести тип из аргумента по умолчанию?

    В C ++ 03 спецификация явно запрещает использовать аргумент по умолчанию для вывода аргумента шаблона (C ++ 03 §14.8.2 / 17):

    Параметр типа шаблона не может быть выведен из типа аргумента по умолчанию функции.

    В C ++ 11 вы можете предоставить аргумент шаблона по умолчанию для шаблона функции:

     template  void bar(int a, T b = 0.0f) { } 

    Однако аргумент шаблона по умолчанию требуется. Если аргумент шаблона по умолчанию не указан, аргумент функции по умолчанию все еще не используется для вывода аргумента шаблона. В частности, применяется следующее (C ++ 11 14.8.2.5/5):

    Невыводимые контексты:

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

    В общем, будут достигнуты некоторые технические трудности. Помните, что аргументы по умолчанию в шаблонах не создаются, пока они не понадобятся. Рассмотрим следующее:

     template void f(U p = T::g()); // (A) template T f(long, int = T()); // (B) int r = f(1); 

    Это разрешено сегодня, выполняя (помимо прочего) следующие шаги:

    1. попытаться вывести параметры шаблона для кандидатов (A) и (B); это не выполняется для (A), которое поэтому устраняется.
    2. выполнять разрешение перегрузки; (B) выбрано
    3. сформировать вызов, создавая экземпляр аргумента по умолчанию

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

    Хорошей причиной может быть то, что

     void foo(bar, xyzzy = 0); 

    похож на пару перегрузок.

     void foo(bar b) { foo(b, 0); } foo(bar, xyzzy); 

    Более того, иногда целесообразно реорганизовать его на такие:

     void foo(bar b) { /* something other than foo(b, 0); */ } foo(bar, xyzzy); 

    Даже когда он написан как один, он по-прежнему как две функции в одном, ни один из которых не является «предпочтительным» в любом смысле. Вы вызываете функцию с одним аргументом; функция с двумя аргументами является фактически другой функцией. Обозначение аргумента по умолчанию просто объединяет их в одно.

    Если перегрузка должна была иметь поведение, о котором вы просите, то для согласованности он должен работать в том случае, когда шаблон разбивается на два определения. Это не имеет смысла, потому что тогда вычет будет тянуть типы от несвязанной функции, которая не называется! И если это не было реализовано, это означало бы, что перегрузка различных длин списка параметров становится «гражданином второго сорта» по сравнению с «аргументом по умолчанию».

    Хорошо, если разница между перегрузками и дефолтом полностью скрыта для клиента.

    Interesting Posts

    MediaPlayer setDataSource, лучше использовать путь или FileDescriptor?

    Тестирование. Невозможно разрешить все параметры для (ClassName)

    C # https логин и файл загрузки

    Что такое ошибка INSTALL_PARSE_FAILED_NO_CERTIFICATES?

    Почему pinging «drive» получает ответы от 127.0.53.53?

    Как определить текущую операционную систему с помощью Node.js

    Какой тип данных MySQL следует использовать для широты / долготы с 8 десятичными знаками?

    Несколько директив , запрашивающих новую / выделенную область

    Custom.css перестает работать в 32.0.1700.76 м Обновление Google Chrome

    Получить Visual Studio для запуска шаблона T4 для каждой сборки

    Передает ли объект C ++ в свой собственный конструктор законным?

    Почему результат 1/3 == 0?

    Когда имя массива или имя функции «преобразуется» в указатель? (в С)

    Пакетное преобразование папки .mkv-файлов в файлы .m4v с использованием ffmpeg в MAC OS X

    Ошибка Weird MSC 8.0: «Значение ESP не было должным образом сохранено в вызове функции …»

    Давайте будем гением компьютера.