boost :: ref是如何工作的?

时间:2014-10-08 10:27:33

标签: c++ boost

在StackOverflow上阅读了几对解释之后,我仍然不知道它是如何工作的以及它的用途。我看到的演示都使用boost::reference_wrapper<int>作为类型,也就是说,它们都包装int,并且它们都运行前缀op ++来显示它如何影响函数模板中的包装int。一位专家表示,如果在ref包装器上调用op ++,则ref包装器将被强制转换为包装对象,但似乎它不是如何工作的。请参阅以下示例,该示例演示了如果包装对象不是int会发生什么。您可能需要在阅读代码之前编译它,以便节省宝贵的时间。

// Build int version: g++ thisFile.cpp -Wall
// Build CFoo version: g++ -std=c++11 thisFile.cpp -DDONT_USE_INT -Wall
#include <boost/ref.hpp>
#include <iostream>
using namespace boost;
using namespace std;

class CFoo
{
public:
    CFoo(int val) : m_val(val) {}
    CFoo& operator++(void) {
        ++m_val;
        return *this;
    }
private:
    int m_val;
friend ostream & operator<<(ostream& ostrm, const CFoo& param);
};

template <typename T>
void a_func_tmpl(T param)
{
    ++param;
}

ostream & operator<<(ostream& ostrm, const CFoo& param)
{
    ostrm << param.m_val;
    return ostrm;
}

int main(int argc, char *argv[])
{
#if defined(DONT_USE_INT)
    CFoo obj(0);
#else
    int obj(0);
#endif
    a_func_tmpl(obj);
    cout << obj << endl;
    a_func_tmpl(ref(obj));
    cout << obj << endl;
    return 0;
}

以下是编译的输出。

$ g++ -std=c++11 thisFile.cpp -Wall
$ ./a.out
0
1
$ g++ -std=c++11 thisFile.cpp -DDONT_USE_INT -Wall
thisFile.cpp: In instantiation of ‘void a_func_tmpl(T) [with T = boost::reference_wrapper<CFoo>]’:
thisFile.cpp:40:22:   required from here
thisFile.cpp:22:2: error: no match for ‘operator++’ (operand type is ‘boost::reference_wrapper<CFoo>’)
  ++param;
  ^

如您所见,如果包装类型为int,它确实有效,但如果类型不是int,即使包装类型提供op ++,也会发生编译时错误。如果有人可以解释在包装的ref上调用包装类型的方法时会发生什么事情,我将非常感激(我已经坚持了2天T_T)。提前致谢。 m(_ _)m

3 个答案:

答案 0 :(得分:7)

reference_wrapper 非常简单,理解其工作原理的最简单方法就是查看代码。创建reference_wrapper的生成器函数refcref甚至更简单,再看一下它们的定义。

理解它的用途也很简单:reference_wrapper的用途是通过引用传递变量,通用API通常按值获取参数。那就是它。

当您在转发API中包含某些函数或仿函数时,这非常有用,并且希望确保转发API传递引用而不是值。

例如,boost::bind将其参数复制到它返回的可调用对象中,然后调用目标函数将复制的对象作为参数传递。

e.g。当您致电boost::bind(&func, i)时,它会返回一个包含&func副本和i副本的仿函数。当您调用该仿函数时,它会使用func副本调用i。因此,如果函数采用引用,则该引用将绑定到i的内部副本,而不是i本身。所以如果你有:

void func(int& i) { ++i; }
int i = 0;
auto bound = boost::bind(&func, i);
bound();
assert( i == 1 );  // FAILS!

断言将失败,因为传递给int的{​​{1}}不是func,而是存储在i内的i副本。

如果您确实希望使用引用调用绑定函数,则需要像值一样可复制但实现引用语义的内容,这是bound的来源:

reference_wrapper

现在void func(int& i) { ++i; } int i = 0; auto bound = boost::bind(&func, boost::ref(i)); bound(); assert( i == 1 ); // passes 创建引用ref(i)的{​​{1}},因此reference_wrapper<int>包含i的副本,同时引用bound 。当您调用reference_wrapper<int>时,它会将i传递给bound,这会触发隐式转换为reference_wrapper<int>,以便引用绑定到func和{{ 1}}根据需要增加。

使用int&的其他示例包括ii(以及Boost等效项)。他们复制他们的参数,然后将它们作为rvalues传递给包装的目标仿函数,所以如果仿函数有左值参考参数,那么你必须使用reference_wrapper来代码甚至编译。

在您的std::thread示例中使用std::async与预期用途并不匹配,因为该功能没有引用,您也没有通过它来调用它无论如何都会衰减对值的引用的通用API。就个人而言,我不会过分担心为什么你的例子在一个案例中而不是在另一个案例中起作用,因为它无论如何都不是reference_wrapper的预期用例。理解 的含义更为重要,因此您可以在必要时在适当的位置使用它。

答案 1 :(得分:2)

您对reference_wrapper<>的使用和理解似乎确实是正确的。但是,你偶然发现了另一个问题,这个问题掩盖了这一点。

问题是隐式reference_wrapper<CFoo>参数没有从CFoo&this的隐式转换。在这种情况下,需要找到operator++。但是,它应该可以正常工作,具有相同的独立功能:

void bar(CFoo& foo)
{
  ++foo;
}

template <typename T>
void a_func_tmpl(T param)
{
    bar(param); // This allows the implicit conversion
}

或者,您可以将operator ++实现为独立函数:

class CFoo
{
public:
  CFoo (int val) : m_val (val) {}

private:
  int m_val;
  friend ostream & operator<<(ostream& ostrm, const CFoo& param);
  friend CFoo& operator++(CFoo& foo);
};

CFoo& operator++(CFoo& foo) {
  ++foo.m_val;
  return foo;
}

唯一的问题是,如果你在类中定义它,编译器不知道它需要从reference_wrapper<CFoo>转换为CFoo&以找到operator++。转换是可用的,但不会被要求。

答案 2 :(得分:0)

代码失败,因为++param正在调用boost::reference_wrapper<CFoo>::operator++(因为那是你传递的内容)没有定义。

reference_wrapper<T>的界面有一个转换运算符T&,但编译器没有办法推断出你的意思。 x++表示调用x :: operator ++&#39;,而不是&#39;找到任何旧版本的operator ++,我可以强制x进入&#39;

++(static_cast<T&>(param))

  T& p = param;
  ++p;