У меня есть вопросы по этой программе:
#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&
)?
x = y;
в обоих случаях?Ответы:
Well
ref
создает объект соответствующегоreference_wrapper
типа для хранения ссылки на объект. Что означает, когда вы подаете заявку:auto r = ref(x);
Это возвращает a,
reference_wrapper
а не прямую ссылку наx
(т.е.T&
). Этоreference_wrapper
(т.е.r
) вместо этого выполняетсяT&
.A
reference_wrapper
очень полезен, когда вы хотите имитироватьreference
объект, который можно скопировать (он может быть как копируемым, так и копируемым ).В C ++, как только вы создаете ссылку (скажем
y
) на объект (скажемx
), тогдаy
иx
используете тот же базовый адрес . Более того,y
не может ссылаться на какой-либо другой объект. Также вы не можете создать массив ссылок, т.е. такой код выдаст ошибку:#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 */
Здесь мы узнаем три вещи:
reference_wrapper
Объект (здесьr
) можно использовать для создания массива ссылок , которые не удались сT&
.r
на самом деле действует как реальная ссылка (посмотрите, какr.get()=70
изменилось значениеy
).r
не то же самое,T&
ноr.get()
есть. Это означает , чтоr
держитT&
т.е. как предполагает его название является оберткой ссылкиT&
.Надеюсь, этого ответа более чем достаточно, чтобы объяснить ваши сомнения.
источник
reference_wrapper
можно переназначить , но он не может «содержать ссылку на более чем один объект». 2/3: Справедливое замечание о том, где.get()
это уместно, но без суффиксовr
можно использовать так же, как иT&
в случаях, когдаr
преобразованиеoperator
может быть вызвано однозначно - поэтому.get()
во многих случаях нет необходимости вызывать , включая несколько в вашем коде (который трудно читать из-за отсутствия мест).reference_wrapper
может содержать массив ссылок, если вы не уверены, вы можете попробовать это сами. Плюс.get()
используется, когда вы хотите изменить значение объекта,reference_wrapper
который хранится, т.е.r=70
является незаконным, поэтому вы должны использоватьr.get()=70
. Попробуйте сами !!!!!!int a[4]{1, 2, 3, 4}; int (&b)[4] = a;
? reference_wrapper здесь не особенный, поскольку nativeT&
действительно работает.wrapper
может идти в контейнере. Это удобно, но я думаю, что люди неверно истолковывают его как более продвинутый, чем он есть на самом деле. Если мне нужен массив «ссылок», я обычно пропускаю посредникаvector<Item *>
, что иwrapper
сводится к ... и надеюсь, что пуристы против указателей не найдут меня. Убедительные варианты использования для него разные и более сложные.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&
.источник
std::ref(T)
возвращает файлstd::reference_wrapper
. Это немного больше, чем обернутый указатель, но он распознается библиотекой как «эй, я должен быть ссылкой! Пожалуйста, превратите меня в одного, как только вы закончите передавать меня».Ссылка (
T&
илиT&&
) - это особый элемент в языке C ++. Он позволяет управлять объектом по ссылке и имеет особые варианты использования в языке. Например, вы не можете создать стандартный контейнер для хранения ссылок:vector<T&>
он плохо сформирован и генерирует ошибку компиляции.С
std::reference_wrapper
другой стороны, A - это объект C ++, способный хранить ссылку. Таким образом, вы можете использовать его в стандартных контейнерах.std::ref
- это стандартная функция, которая возвращает в качествеstd::reference_wrapper
аргумента. По той же идееstd::cref
возвращаетсяstd::reference_wrapper
к константной ссылке.Одно интересное свойство a
std::reference_wrapper
- это то, что у него естьoperator T& () const noexcept;
. Это означает, что даже если это настоящий объект , он может быть автоматически преобразован в ссылку, которую он держит. Так:operator T& () const noexcept;
можно использовать везде, где вы можете использовать ссылку, потому что он будет автоматически преобразован в нее.источник
operator T& ()
которых не упоминалось в двух других ответах.