Ответ 1
TL; DR: Это заданное поведение в соответствии со стандартом. Вывод аргумента шаблона имеет специальное правило для вывода аргументов шаблона при выборе адреса шаблона функции, что позволяет перенаправить ссылки на работу, как ожидалось. Такого правила для шаблонов функций преобразования нет.
Примечание: это выглядит как область, для которой пока еще никто не написал предложение.Если кто-то пишет предложение для этого, представляется вероятным, что это может быть сделано для работы в будущем.
.... Для общей лямбда без лямбда-захвата тип закрытия имеет шаблон функции преобразования для указателя на функцию. Шаблон функции преобразования имеет тот же самый изобретенный список параметров шаблона, и указатель на функцию имеет те же типы параметров, что и шаблон оператора вызова функции. Тип возврата указателя на функцию должен вести себя так, как если бы это был спецификатор decltype, обозначающий возвращаемый тип соответствующей функции шаблона оператора вызова функции.
добавлен акцент
Это указывает, что аргументы шаблона и типы параметров функций должны копироваться индивидуально:
// simplified version of the example in [expr.prim.lambda]/8
struct Closure {
template <typename T>
void operator()(T&& t) const {
/* ... */
}
template <typename T>
static void lambda_call_operator_invoker(T&& t) {
Closure()(std::forward<T>(t));
}
// Exactly copying the template parameter list and function parameter types.
template <typename T>
using fn_type = void(*)(T&&);
// using fn_type = void(*)(T); // this compiles, as noted later
template <typename T>
operator fn_type<T>() const {
return &lambda_call_operator_invoker<T>;
}
};
Это не удается скомпилировать для всех трех Clang, GCC и MSVC, что, безусловно, может удивить, поскольку мы ожидали, что сверкание ссылок произойдет в аргументе T&&
.
Однако стандарт не поддерживает это.
Важными частями стандарта являются [temp.deduct.funcaddr] ( вывод аргументов шаблона с адресом шаблона функции) и [temp.deduct.conv] ( вывод аргументов шаблона функции преобразования). Критически, [temp.deduct.type] конкретно упоминает [temp.deduct.funcaddr], но не [temp.deduct.conv].
Некоторые термины, используемые в стандарте:
- P - тип возврата шаблона преобразования или тип шаблона функции
- A - это тип, который мы пытаемся преобразовать в "
Аналогично, если P имеет форму, содержащую (T), то каждый тип параметра P i соответствующего списка параметров-параметра ([dcl.fct]) P сравнивается с соответствующим типом параметра A i соответствующего параметра- type-list A. Если P и A являются типами функций, которые возникли из вывода при принятии адреса шаблона функции ([temp.deduct.funcaddr]) или при выводе аргументов шаблона из объявления функции ([temp.deduct.decl ]), а P i и A i - параметры списка параметров типа верхнего уровня для P и A, соответственно, P i корректируется, если это ссылка пересылки ([temp.deduct.call]), и A i является ссылка lvalue, и в этом случае тип P i изменяется как тип параметра шаблона (т.е.
T&&
изменяется на простоT
).
добавлен акцент
Это специально вызывает обращение к адресу шаблона функции, поскольку перенаправление ссылок просто работает. Подобной ссылки на шаблоны функций преобразования нет.
Повторяя пример раньше, если мы изменим fn_type
на void(*)(T)
, это та же самая операция, которая описана здесь в стандарте.