Что выведется на консоль и почему это вообще компилируется? [C++]
Наткнулся на такой с++ код, мне не понятны две вещи:
- Почему это вообще компилируется без std::forward?
- Почему вывод в обратном порядке, то есть "if"?
Вот код:
#include <iostream>
void func(float&&) {
std::cout << "f";
}
void func(int&&) {
std::cout << "i";
}
template <typename T>
void test(T&& v) {
func(v);
}
int main() {
test(1.0f);
test(2);
}
Мои рассуждения зашли в тупик в таком порядке:
- К примеру, мы передали rvalue значение "2" в функцию test, которая принимает универсальную ссылку
- Тип T вывелся как int и затем на него нацепилось &&, на данном этапе имеем int&&
- Мы передаем в функцию func переменную v ( func(v) ), где v уже имеет категорию значения lvalue (любое обращение к переменной по имени - это lvalue)
- Ищем функцию func с подходящей сигнатурой, которая может принять lvalue и не находим
- Тупик...
Ответы (1 шт):
Достаточно интересный вопрос, я сначала тоже немного растерялся, но если подумать, то все не так уж и сложно.
Как вы и описали первые пункты они все верны. Но что происходит дальше?
Как вы верно заметили в 3 пункте
/*
Именно при передаче в функцию наше rvalue, превращается -> T&& v,где
значение "value" этого выражения = lvalue, а тип это ссылка на rvalue(т.к.
это универсальная ссылка и мы передали rvalue)
*/
template <typename T>
void test(T&& v) {
func(v);
}
Надеюсь вы прочитали комментарии.. Продолжая мысль, нам не важен тип, а важно что сама v
это lvalue, поэтому при вызове func(v)
он ищет функцию, которая принимает ссылку на lvalue, но такой нет! По сути в этот момент, если взять классический пример, то он вообще не должен компилироваться:
void func(int&& a) {
std::cout << "i&& " << a << std::endl;
}
int main(){
int a = 10;
func(a);
}
Но это с++, так что нет ничего удивительного, что он такое пропускает, если говорить кратко, это ситуация близка к неопределенному поведению(ub)
. Добавить просто нечего, лучше не пишите так вообще!
Теперь, что можно сделать, чтобы решить эту проблему:
- использовать
std::move()
template <typename T>
void test(T&& v) {
func(std::move(v));
}
- использовать
std::forward<T>()
template <typename T>
void test(T&& v) {
func(std::forward<T>(v));
}
Но в случает использования std::forward<T>()
, если мы передадим lvalue у нас опять будет ub, так что предпочтительнее использоваться std::move()
3. Добавить перегрузку для lvalue
void func(float& a) {
std::cout << "f& " << a << std::endl;
}
void func(int& a) {
std::cout << "i& " << a << std::endl;
}