什么时候需要使用std::ref?

48
考虑一下:
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 );
    }
  }
};

2
也可以在https://dev59.com/YGUp5IYBdhLWcg3wF0dq中找到一个很好的问答示例。 - ikku100
4个回答

47

使用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;
}
< p > std::thread 构造函数会复制提供的值,而不会将其转换为期望的参数类型(在这种情况下是引用类型,请参见update())。因此,我们需要将确实需要引用的参数包装在std::ref


12
std::thread t1(update, data); 这行代码可以编译通过,但问题是它没有改变数据的值。 - Mohammad Roohitavaf
1
@MohammadRoohitavaf 它无法编译!变量"data"必须被包装在std::ref(data)中,以生成传递给t1的rvalue。如果函数"update"中的参数"data"是"type 'const int&'"类型,那么你就是正确的。但是,std::thread会在内部复制参数,并实际上不引用传递的变量"data"。 - Degoah

26

std::ref 并不创建引用,所以在你的代码样例中它并不产生你期望的结果。 std::ref 创建一个行为类似于引用的对象。例如,在您想要实例化一个函数对象并将其引用式版本传递给标准库算法时,它可能很有用。由于算法按值接受函数对象,因此可以使用 std::ref 来包装该函数对象。


23
  • 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

那么,如果删除ref,它应该会抛出一个错误。为什么它没有抛出错误呢?你有什么想法吗? - ritter
2
@Frank:元组可以由任何人类可能的东西构建。你最终会返回对本地变量的引用,即a的本地副本的引用。使用ref,你会返回对原始函数参数的包装引用。 - Kerrek SB
我做到了 :-) 这很扭曲。在函数体中,a指的是传递到函数中的实例。但是当使用make_tuple时,它被视为局部变量。这很容易出错。有什么建议可以避免这种错误的做法吗?;-) - ritter
@Frank:你熟悉 std::tuple 的工作原理吗?它是一个非常有趣的模板,涉及到 C++ 的许多角落... - Kerrek SB
3
严格来说,std::make_tuple(ref(a)) 的结果将是 std::tuple<A const&>,而不是 std::tuple<std::reference_wrapper<const A>>。标准库中许多将它们的参数衰减的部分会将 std::reference_wrapper<T> 特殊处理为 T&。它们可以通过通常用 std::decay 规定,即使 std::decay 本身并不执行该转换(令人感到有些沮丧)。 - Luc Danton
显示剩余2条评论

3

回答标题中的问题 (什么情况下需要使用 std::ref?):另一个使用 std::ref 有用的情况是当循环遍历对象引用列表并修改它们时:

std::vector<int> v1, v2;
  
void test() {
  for (std::vector<int>& vv : 
    // Compiles
    { std::ref(v1), std::ref(v2) } 
  
    // Compiler rejects this with:
    //   binding reference of type 'vector<...>' to value of 
    //   type 'const vector<...>' drops 'const' qualifier 
    // { v1, v2} 
  ) {
      vv.push_back(3);
  }
}

如果在列表中不使用std::ref,那么这些对象将被视为const,并且无法进行修改(另请参见https://godbolt.org/z/Ta6YM31KM)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接