Ответ 1
Lvalues настоятельно предпочитают привязку к ссылкам lvalue, и аналогичные ссылки rvalue сильно предпочитают связывание с ссылками rvalue. Модифицируемые выражения слабо предпочитают связывание с неконстантной ссылкой.
Итак, когда ваш компилятор делает разрешение перегрузки, он проверяет, есть ли перегрузка, которая принимает ссылку rvalue, потому что это очень сильно. В этом случае, поскольку разложение является изменяемым значением r, поэтому выигрывает эталонная перегрузка rvalue.
Фактически используется для ссылок const rvalue, их можно использовать, чтобы убедиться, что что-то не привязывает к rvalue. Помните, что rvalue связывается с константой lvalue, поэтому, если вы это сделали:
template <typename T> void foo(const T& bar) { /* ... */ }
И вызывается функция с помощью:
foo(createVector());
Все будет хорошо. Однако иногда желательно обеспечить, чтобы вы могли передавать только lvalues функции (это имеет место для std::ref
для одного). Вы можете добиться этого, добавив перегрузку:
template <typename T> void foo(const T&&) = delete;
Помните, что rvalues сильно предпочитают привязку к rvalue-ссылкам, а модифицируемые выражения предпочитают слабое связывание с неконстантными ссылками. Поскольку у нас есть константа rvalue-reference, это в основном означает, что каждое одно rvalue будет связываться с этим, поэтому, если вы попытаетесь передать rvalue на foo()
, ваш компилятор даст ошибку. Это единственный способ достичь такой функциональности и, следовательно, иногда полезен.