提升::ref 如何工作
How does boost::ref work?
在阅读了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 上调用包装类型的方法时真正会发生什么,将不胜感激(T_T 年我一直被困在这个上面(。提前谢谢。m(_ _)m
reference_wrapper
非常简单,理解它如何工作的最简单方法就是查看代码。创建reference_wrapper的生成器函数、ref
和cref
更简单,同样,只需查看它们的定义即可。
理解它的用途也非常简单:reference_wrapper
的预期用途是通过通常按值获取参数的通用 API 通过引用传递变量。就是这样。
当您在转发 API 中包装了某些函数或函子,并希望确保转发 API 传递引用而不是值时,这很有用。
例如,boost::bind
将其参数复制到它返回的可调用对象中,然后调用目标函数将复制的对象作为参数传递。
例如,当您调用boost::bind(&func, i)
时,它会返回一个函子,其中包含 &func
的副本和 i
的副本。当您调用该函子时,它会使用 i
的副本调用 func
。因此,如果函数采用引用,则该引用将绑定到i
的内部副本,而不是i
自身。因此,如果您有:
void func(int& i) { ++i; }
int i = 0;
auto bound = boost::bind(&func, i);
bound();
assert( i == 1 ); // FAILS!
断言将失败,因为传递给func
的int
不是i
而是存储在bound
中的i
的副本。
如果你真的希望使用引用调用绑定函数,你需要一些可复制的东西,如值,但实现引用语义,这就是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)
创建了一个引用i
的reference_wrapper<int>
,因此bound
包含该reference_wrapper<int>
的副本,也引用i
。当你调用bound
时,它会将reference_wrapper<int>
传递给func
,这会触发对int&
的隐式转换,以便引用绑定到i
,并且i
会根据需要递增。
使用reference_wrapper
的其他示例是std::thread
和std::async
(以及 Boost 等效项(。它们复制它们的参数,然后将它们作为右值传递给包装的目标函子,因此如果函子具有左值引用参数,则必须使用 reference_wrapper
才能编译代码。
将reference_wrapper
与a_func_tmpl
示例一起使用并不真正符合预期用途,因为该函数不带引用,并且您不会通过通用 API 调用它,该 API 无论如何都会将引用衰减为值。就个人而言,我不会太担心为什么您的示例在一种情况下有效而在另一种情况下无效,因为它无论如何都不是reference_wrapper
的预期用例。更重要的是了解它的用途,以便您可以在必要时在适当的位置使用它。
你对reference_wrapper<>
的使用和理解似乎是正确的。但是,您偶然发现了另一个问题,这掩盖了这一点。
问题是隐式this
参数没有从reference_wrapper<CFoo>
到CFoo&
的隐式转换。在这种情况下,需要这样做才能找到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++
。转换可用,但未要求转换。
代码失败++param
因为调用没有定义的boost::reference_wrapper<CFoo>::operator++
(因为这是您传递的(。
reference_wrapper<T>
的接口有一个转换运算符来T&
,但编译器没有办法推断这就是你的意思。 x++
的意思是"调用 x::operator++",而不是"找到我可以强迫 x 进入的任何旧版本的运算符++">
尝试
++(static_cast<T&>(param))
或
T& p = param;
++p;
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 当我在第一个循环中使用"auto"时,它工作正常,但是使用"int"它会给出错误,为什么?
- 当 int 方法工作正常时,void 方法有何不同,或者为什么我不能调用 void 方法?
- sdl软件渲染器不工作,工作在硬件加速的一个