Тип распада - что это такое и почему оно там?
Я был удивлен, что распад типа не очень хорошо объяснен в SO или где-либо еще, возможно, я не искал, используя правильные термины, или, возможно, я не понимаю все правильно. Мой вопрос: что это такое, как (почему) оно туда попало и каковы его правила?
Если вам интересно, почему я спрашиваю, ниже приведен мой рассказ о распаде типа всхлипа (ноне предмет вопроса):
Недавно я боролся с простыми шаблонами и хотел сделать что-то вроде этого:
template <class FunObj>
double DoStuff(FunObj fob) // note that this returns double, not FunObj, as e.g. std::for_each() does
{ /* ... */ }
struct MyFunObj {
mutable size_t num_invoked; // collect usage statistics
MyFunObj()
:num_invoked(0)
{}
bool operator ()(double some_arg) const
{
++ num_invoked;
return sin(some_arg) < .5;
}
};
Это все хорошо, есть функция шаблона, ожидающая объект функции, и объект функции. Функция была разработана таким образом, чтобы не возвращать состояние объекта функции (как часто делают аналогичные функции; вместо этого она возвращала результат некоторых сложных вычислений), поэтому я подумал, что специализирую ее для ссылки на объект. Но затухание типа мешает (или, по крайней мере, я так понимаю):
MyFunObj fob;
DoStuff(fob); // passed by value, usage statistics are lost (no surprise here)
MyFunObj &ref = fob;
DoStuff(ref); // MyFunObj& decays to MyFunObj, usage statistics are lost
DoStuff(static_cast<MyFunObj&>(fob)); // again, hampered by type decay
Я знаю, как решить эту проблему, поэтому, пожалуйста, не отвечайте, как. Меня интересует только распад типа как таковой, и это здесь только в качестве мотивации моего вопроса / иллюстративного примера. Если хотите, можете опубликовать ненависть за мое использование mutable
,
1 ответ
Я не думаю, что ваш вопрос показывает пример распада типа. Термин "распад" обычно используется для обозначения стандартных преобразований функция-указатель, массив-указатель и lvalue-to-rvalue. Что вас поражает, так это правила вывода аргументов шаблона.
Дело в том, что вывод аргументов шаблона основан на типах, а не на категориях значений. Тип ref
в вашем примере это MyFunObj
и это lvalue этого типа (Кстати, то же самое верно для fob
). Так как вычет работает на типах, FunObj
выводится на тип ref
что действительно MyFunObj
,
До C++11 не было способа вывести ссылочный тип. C++11 изменил это с помощью "пересылки ссылок". Они представили особый случай, когда передача lvalue типа U
к параметру функции, объявленной как T&&
(где T
параметр шаблона шаблона функции) использует U&
как тип для вычета вместо U
, Но это особое правило, исключение. Обычно категории значений не играют роли в выводе типов.