簡體   English   中英

boost :: 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上調用包裝類型的方法時會發生什么事情,我將非常感激(我已經堅持了2天T_T)。 提前致謝。 m(_ _)m

reference_wrapper 非常簡單,了解其工作原理的最簡單方法就是查看代碼。 創建reference_wrapper的生成器函數, refcref甚至更簡單,再看一下它們的定義。

理解它的用途也很簡單: reference_wrapper用途是通過引用傳遞變量,通用API通常按值獲取參數。 而已。

當你在轉發API中包含一些函數或函子時,這很有用,並且希望確保轉發API傳遞引用而不是值。

例如, boost::bind其參數復制到它返回的可調用對象中,然后調用目標函數將復制的對象作為參數傳遞。

例如,當你調用boost::bind(&func, i)它會返回一個包含&func副本和i副本的仿&func 當您調用該仿函數時,它會使用i副本調用func 因此,如果函數接受引用,則該引用將綁定到i的內部副本,而不是i本身。 所以如果你有:

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

斷言將失敗,因為傳遞給funcint不是i而是存儲在boundi的副本。

如果您確實希望使用引用調用綁定函數,則需要像值一樣可復制但實現引用語義的內容,這是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)創建reference_wrapper<int>引用ibound包含的副本reference_wrapper<int> ,也參照i 當你調用bound它會將reference_wrapper<int>傳遞給func ,這會觸發隱式轉換為int& ,這樣引用就會綁定到i ,並且i會根據需要增加。

您將使用reference_wrapper其他示例是std::threadstd::async (以及Boost等效項)。 他們復制他們的參數,然后將它們作為rvalues傳遞給包裝的目標函子,所以如果函子有左值引用參數,那么你必須使用reference_wrapper來代碼甚至編譯。

在您的a_func_tmpl示例中使用reference_wrapper並不能與預期用途完全匹配,因為該函數不會引用,並且您不會通過通用API調用它,無論如何都會衰減對值的引用。 就個人而言,我不會太擔心為什么你的例子在一個案例中而不是在另一個案例中工作,因為它無論如何都不是reference_wrapper的預期用例。 明白這什么意思為這是更重要的,這樣你就可以在適當的地方,必要時使用它。

您對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++如果你在類中定義它)。 轉換可用,但不要求。

代碼失敗是因為++param正在調用boost::reference_wrapper<CFoo>::operator++ (因為那是你通過的),沒有定義。

reference_wrapper<T>的接口有一個轉換運算符到T& ,但編譯器沒有辦法推斷出那就是你的意思。 x++表示'調用x :: operator ++',而不是'找到我可以強制x進入的任何舊版本的operator ++'

嘗試

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

要么

  T& p = param;
  ++p;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM