ссылка — C ++ Разница между std :: ref (T) и T & amp;?

У меня есть несколько вопросов относительно этой программы:

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
auto r=ref(x);
cout<<boolalpha;
cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
int x=5;
foo (x);
return 0;
}

Выход:

false

Я хочу знать, если std::ref не возвращает ссылку на объект, тогда что он делает? По сути, в чем разница между:

T x;
auto r = ref(x);

а также

T x;
T &y = x;

Кроме того, я хочу знать, почему существует эта разница? Зачем нам std::ref или же std::reference_wrapper когда у нас есть ссылки (т.е. T&)?

50

Решение

Что ж ref конструирует объект соответствующего reference_wrapper тип для хранения ссылки на объект. Что означает, когда вы подаете заявление:

auto r = ref(x);

Это возвращение reference_wrapper & не прямая ссылка на x (ie T&), это reference_wrapper (ie r) вместо этого держит T&,

reference_wrapper очень полезно, когда вы хотите подражать reference объекта, который может быть скопирован (это как copy-constructible а также copy-assignable).

В C ++, когда вы создаете reference (say y) для object (say x), затем y & x делить то же самое base address, Более того, y не может ссылаться на любой другой объект. Также вы не можете создать array of references то есть код, подобный этому, выдаст ошибку:

#include <iostream>
using namespace std;
int main()
{
int x=5, y=7, z=8;
int& arr[] {x,y,z};    // error: declaration of 'arr' as array of references
return 0;
}

Однако это законно:

#include <iostream>
#include <functional>  // for reference_wrapper
using namespace std;
int main()
{
int x=5, y=7, z=8;
reference_wrapper<int> arr[] {x,y,z};
for (auto a:arr)
cout<<a<<" ";
return 0;
}
/* OUTPUT :-
5 7 8
*/

Говоря о вашей проблеме с cout<<is_same<T&,decltype(r)>::value;, решение: —

cout<<is_same<T&,decltype(r.get())>::value;  // will yield true

Позвольте мне показать вам программу: —

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
int main()
{
cout<<boolalpha;
int x=5, y=7;;
reference_wrapper<int> r=x;   // or auto r = ref(x);
cout<<is_same<int&, decltype(r.get())>::value<<"\n";
cout<<(&x==&r.get())<<"\n";
r=y;
cout<<(&y==&r.get())<<"\n";
r.get()=70;
cout<<y;
return 0;
}
/* Ouput :-
true
true
true
70
*/

Смотрите здесь, мы узнаем три вещи:

  1. reference_wrapper объект (здесь r) может быть использован для создания array of references что было невозможно с T&,

  2. r на самом деле действует как настоящий reference ( Смотри как r.get()=70 изменил значение y).

  3. r не то же самое, что T& но r.get() является. Это означает, что r держит T& то есть как следует из названия это wrapper around reference T&,

Я надеюсь, что этого ответа более чем достаточно, чтобы объяснить ваши сомнения.

46

Другие решения

std::reference_wrapper признается стандартными средствами, чтобы иметь возможность передавать объекты по ссылке в контексте передачи по значению.

Например, std::bind может взять в std::ref() передайте его по значению и позже распакуйте обратно в ссылку.

void print(int i) {
std::cout << i << '\n';
}

int main() {
int i = 10;

auto f1 = std::bind(print, i);
auto f2 = std::bind(print, std::ref(i));

i = 20;

f1();
f2();
}

Этот фрагмент выводит:

10
20

Значение i был сохранен (взят по значению) в f1 в тот момент, когда он был инициализирован, но f2 сохранил std::reference_wrapper по стоимости, и, следовательно, ведет себя так, как это потребовалось в int&,

36

Ссылка (T& или же T&&) является специальным элементом в языке C ++. Это позволяет манипулировать объектом по ссылке и имеет особые случаи использования на языке. Например, вы не можете создать стандартный контейнер для хранения ссылок: vector<T&> плохо сформирован и генерирует ошибку компиляции.

std::reference_wrapper с другой стороны, это объект C ++, способный содержать ссылку. Таким образом, вы можете использовать его в стандартных контейнерах.

std::ref это стандартная функция, которая возвращает std::reference_wrapper на его аргумент. В той же идее std::cref возвращается std::reference_wrapper к постоянной ссылке.

Одно интересное свойство std::reference_wrapperчто у него есть operator T& () const noexcept;, Это означает, что даже если это настоящий объект, он может быть автоматически преобразован в ссылку, которую он держит. Так:

  • так как это объект, назначаемый для копирования, его можно использовать в контейнерах или в других случаях, когда ссылки не разрешены
  • благодаря его operator T& () const noexcept;, он может быть использован везде, где вы можете использовать ссылку, потому что он будет автоматически преобразован в него.
19
По вопросам рекламы [email protected]