Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

optional 引用賦值操作的重綁定語義

如果你對一個未初始化的 optional<T&> 進行賦值,效果將是將它綁定(對於第一次賦值)到某個對象。顯然,這裡沒有其它的選擇。

int x = 1 ;
int& rx = x ;
optional<int&> ora ;
optional<int&> orb(x) ;
ora = orb ; // 現在 'ora' 通過 'rx' 被綁定到 'x'
*ora = 2 ; // 通過 'ora' 修改 'x' 的值
assert(x==2);

如果你對一個C++裸引用進行賦值,賦值的操作將被前轉至被引用的對象;結果是被引用對象的值被改變而引用本身不會被重綁定。

int a = 1 ;
int& ra = a ;
int b = 2 ;
int& rb = b ;
ra = rb ; // 修改 'a' 的值為 'b'
assert(a==b); b = 3 ; assert(ra!=b); // 'ra' 不會重綁定到 'b'

現在,如果你對一個已初始化的 optional<T&> 進行賦值,結果將是重綁定到新的對象,而不是對引用物進行賦值。這和C++裸引用是不一樣的。

int a = 1 ;
int b = 2 ;
int& ra = a ;
int& rb = b ;
optional<int&> ora(ra) ;
optional<int&> orb(rb) ;
ora = orb ; // 'ora' is rebound to 'b'
*ora = 3 ; // Changes value of 'b' (not 'a')
assert(a==1);
assert(b==3);

原理

已初始化的 optional 引用的賦值操作的重綁定語義提供了對初始化狀態的一致性,這是以違反C++裸引用語義為代價的。確實,optional<U> 盡可能表現得和 U 一樣,不論它是否已初始化;但是如果 UT&, 這樣做就會導致和左值初始化狀態相關的不一致行為。

設想 optional<T&> 將賦值操作前轉至被引用的對象(修改被引用的對象而不是重綁定),考慮一下以下代碼:

optional<int&> a = get();
int x = 1 ;
int& rx = x ;
optional<int&> b(rx);
a = b ;

這個賦值操作會做什麼?

如果 a未初始化的,那麼答案非常清楚:它將被綁定到 x (這樣我們就有了第二個到 x 的引用)。但是如果 a 已經初始化了呢?那麼這個賦值操作可能會修改被引用對像(無論它是什麼)的值,但是這樣做就會和在未初始化情形下的行為不一致。

如果 optional<T&> 的賦值只是象 T& 那樣做,你就永遠不能在沒有顯式處理過原先的已初始化狀態的時候使用 Optional 的賦值,除非你的代碼能夠判斷在這個賦值之後,ab 的別名抑或不是。

即,你不得不為了一致性而進行分別處理。

如果將引用綁定到另一個對像對於你的代碼來說是錯誤的,那麼在第一次的時候通過賦值操作來綁定而不是初始化很可能也是錯誤的。在這種情況下,賦值到一個未初始化的 optional<T&> 應該被禁止。很可能這種情況下前提條件是,左值必須是已初始化的。如果重綁定是錯誤的而第一次的綁定(通過賦值操作)不是,則你可以用一個辨別操作來避免重綁定語義:

assert(!!opt);
*opt=value;

PrevUpHomeNext