Нужно ли нам писать пустые угловые скобки при использовании прозрачных объектов функции std? - программирование
Подтвердить что ты не робот

Нужно ли нам писать пустые угловые скобки при использовании прозрачных объектов функции std?

С выводом аргумента шаблона класса мы можем написать:

std::less Fn;

Однако G++ 8.2 отклоняет этот код:

#include <algorithm>
#include <vector>
#include <functional>

int main()
{
std::vector v= { 1, 3, 2, 7, 5, 4 };

std::sort(v.begin(),v.end(),std::greater());
}

выдает следующую ошибку:

error: cannot deduce template arguments for 'greater' from ()

Клан G++ 7.0 и MSVC 15.8.0 компилируют его без предупреждений. Какой компилятор прав?

4b9b3361

Ответ 1

GCC не прав. Уже есть сообщение об ошибке.

[dcl.type.simple]/2 говорит:

Спецификатор типа вида typename opt nested-name-specier opt template-name является заполнителем для выведенного типа класса ([dcl.type.class.deduct]).

И [dcl.type.class.deduct]/2 говорит:

Заполнитель для выведенного типа класса также может использоваться в type-specier-seq в new-type-id или type-id нового выражения, как простой-type-спецификатор в явном преобразовании типов (функциональная запись ) ([expr.type.conv]), или как спецификатор типа в объявлении параметра параметра шаблона. Заполнитель для выведенного типа класса не должен появляться ни в каком другом контексте.

Такое использование разрешено.


[temp.arg]/4 описывает синтаксическую ошибку, что идентификатор шаблона требуется, но нет <>. Однако здесь std::greater не разрешается как идентификатор шаблона, поэтому абзац не применяется.

Ответ 2

Clang и MSVC верны. Это должно быть правильно сформировано из-за комбинированного эффекта неявно генерируемых руководств по выводам (начиная с С++ 17) и аргумента шаблона по умолчанию.

(акцент мой)

Когда приведение стиля функции или объявление переменной использует имя шаблона первичного класса C без списка аргументов в качестве спецификатора типа, вывод будет выполняться следующим образом:

  • Если C определен, для каждого конструктора (или шаблона конструктора) Ci, объявленного в названном первичном шаблоне (если он определен), вымышленный шаблон функции Fi создается так, что
    • параметры шаблона Fi - это параметры шаблона C, за которыми следуют (если Ci является шаблоном конструктора) параметры шаблона Ci (также включены аргументы шаблона по умолчанию)
    • параметры функции Fi являются параметрами конструктора
    • тип возврата Fi - C, за которым следуют параметры шаблона шаблона класса, заключенного в <>
  • Если C не определен или не объявляет никаких конструкторов, добавляется дополнительный шаблон вымышленной функции, полученный, как указано выше, из гипотетического конструктора C()
  • В любом случае добавляется дополнительный шаблон вымышленной функции, полученный, как указано выше, из гипотетического конструктора C (C), называемый кандидатом на вычет копирования.

Вычисление аргумента шаблона и разрешение перегрузки затем выполняется для инициализации вымышленного объекта гипотетического типа класса, чьи сигнатуры конструктора соответствуют направляющим (за исключением возвращаемого типа) с целью формирования набора перегрузки, и инициализатор предоставляется контекстом в какой вывод аргумента шаблона класса был выполнен, за исключением того, что первая фаза инициализации списка (с учетом конструкторов списка инициализатора) пропускается, если список инициализатора состоит из одного выражения типа (возможно, cv-квалифицированного) U, где U - специализация C или класс, полученный из специализации C.

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

С учетом std::greater(), неявно сгенерированное руководство по дедукции применяется и, наконец, выбирается дополнительная вымышленная функция. В результате разрешения перегрузки применяется аргумент по умолчанию void, тогда выведенный тип будет void. Это означает, что std::greater() должно совпадать с написанием std::greater<void>() или std::greater<>().


Кстати: Gcc не компилируется с std::greater() great std::greater(), но std::greater{} или std::greater g;все в порядке, это может быть ошибка GCC.