Рассмотрим:
std::tuple<int , const A&> func (const A& a)
{
return std::make_tuple( 0 , std::ref(a) );
}
Требуется ли std::ref
для написания правильного и переносимого кода? (Он компилируется без него)
Фон:
Если я удалю std::ref
, мой код строит отлично без каких-либо предупреждений (g++-4.6 -Wall
), но не работает правильно.
В случае интереса определение A
:
struct A {
std::array<int,2> vec;
typedef int type_t;
template<typename... OPs,typename... VALs>
A& operator=(const std::pair< std::tuple<VALs...> , std::tuple<OPs...> >& e) {
for( int i = 0 ; i < vec.size() ; ++i ) {
vec[i] = eval( extract(i,e.first) , e.second );
}
}
};
Ответ 1
-
make_tuple(0, a)
делает tuple<int, A>
.
-
make_tuple(0, ref(a))
делает tuple<int, reference_wrapper<A>>
.
- Вы также можете сказать
tuple<int, A&> t(0, a);
для кортежа, который вы не можете сделать с помощью make_tuple
, или используйте std::tie
.
Ответ 2
std::ref
не делает ссылку, поэтому в вашем примере кода он не делает то, что вы ожидаете. std::ref
создает объект, который ведет себя аналогично ссылке. Это может быть полезно, например, когда вы хотите создать экземпляр функтора и передать его ссылочную версию стандартного библиотечного алгоритма. Поскольку алгоритмы принимают функторы по значению, вы можете использовать std::ref
для обертывания функтора.
Ответ 3
Один из примеров, где std::ref
необходим:
void update(int &data) //expects a reference to int
{
data = 15;
}
int main()
{
int data = 10;
// This doesn't compile as the data value is copied when its reference is expected.
//std::thread t1(update, data);
std::thread t1(update, std::ref(data)); // works
t1.join();
return 0;
}
Конструктор std::thread
копирует предоставленные значения как есть, без преобразования в ожидаемый тип аргумента (который в этом случае является ссылочным типом, см. update()
). Поэтому нам нужно обернуть аргументы, которые действительно должны быть ссылками в std::ref
.