В случае № 2, первый вопрос должен быть: почему func<const int *>(f)
плохо?В конце концов, обычно вы можете использовать int *
там, где требуется const int *
.Причина, по которой он терпит неудачу, заключается в том, что даже если int *
является const int *
(то есть вещь, которую вы можете разыменовать, чтобы получить int
, это вещь, которую вы можете разыменовать, чтобы получить const int
), ссылка на int *
не является ссылкой на const int *
, потому что вы можете назначить "через" его.Если вам было разрешено звонить func<const int *>(f)
, то другая версия func
могла бы, например, назначить что-то, что действительно a const int *
на f
, после чего вы сможете изменить егоуказывает на, что было бы Плохо.
И теперь должно быть очевидно, почему второй const
исправляет его: вы не можете изменить значение f
, передав его по ссылке const
.
Дело № 3 несколько похоже.Опять же, первый вопрос, который нужно задать, заключается не в том, почему ваши третье и четвертое дела работают, а в том, почему ваше второе дело не удалось.Сбой из-за того, что вы не можете получить ссылку на rvalue (&&
) на lvalue (вещь с именем, например f
).Почему бы и нет?Потому что одна из причин этого заключается в том, чтобы иметь возможность получать различное поведение в зависимости от того, является ли передаваемая вещь временной (и, следовательно, безопасной для клюва) или нет, и способ, которым это делается, состоит в том, чтобы запретитьпреобразование lvalue типа T
в rvalue ссылку типа T &&
.(Так что тогда вы можете написать одну функцию, которая принимает T &
, а другую - T &&
, и заставить последнюю делать более эффективную штуковину.)
Но почти все, что вы делаете с этим lvalueперестанет быть lvalue и снова сделает вызов func
легальным.Например, вы можете добавить 0 к нему.Или вы могли бы привести его к другому типу.И, ага!, Это то, что происходит в вашем третьем и четвертом примерах в случае № 3: то, что передается в func
, фактически не само по себе f
, а что-то вроде const_cast<const int *>(f)
, и это больше не lvalue ивсе хорошо.