2016-01-22 99 views
9

不知該標準的部分指定,在下面的代碼段:C++:左值引用和右值的轉換引用

#include <memory> 

class A { }; 

class B : public A { }; 

int main() 
{ 
    std::unique_ptr<B> bptr = std::make_unique<B>(); // (a) 
    std::unique_ptr<A> aptr = std::move(bptr);  // (b) 
    std::unique_ptr<A> &aptr_r = bptr;    // (c) 
    std::unique_ptr<A> &&aptr_rr = std::move(bptr); // (d) 
    return 0; 
} 

(d)編譯和(c)沒有。請在答案中包含標準的相關部分或適當地引用它們。僅供參考,Ubuntu的版本鏗鏘3.6.2-1(標籤/ RELEASE_362 /決賽)(基於LLVM 3.6.2)給我

error: non-const lvalue reference to type 'unique_ptr<A>' cannot 
     bind to a value of unrelated type 'unique_ptr<B>' 
     std::unique_ptr<A> &aptr_r = bptr; 
         ^  ~~~~ 

GCC(Ubuntu的5.2.1-22ubuntu2)5.2 0.1 20151010給我

error: invalid initialization of reference of type ‘std::unique_ptr<A>&’ 
     from expression of type ‘std::unique_ptr<B>’ 
     std::unique_ptr<A> &aptr_r = bptr; 
            ^

編輯:

爲了使我的問題更加清晰,讓我補充

class C { }; 

std::unique_ptr<C> cptr = std::make_unique<C>(); // (e) 
std::unique_ptr<A> &&aptr_rr2 = std::move(cptr); // (f) 

什麼是保持(f)在編譯時(d)呢?顯然AC是無關的,但如果是,當使用的std::unique_ptr構造函數構造臨時兩種(d)和(f)檢測是

template<class U, class E> 
unique_ptr(unique_ptr<U, E> &&u); 
+2

(http://coliru.stacked-crooked.com/a/7e47cee922d95250) – chris

+4

'(C)'不會編譯,因爲'aptr_r'和'bptr'是不同的類型,因此'aptr_'不能成爲'bptr'的引用。它是這樣的:'int x = 0; float&y = x;'。但是'(d)'編譯是因爲目標類型的臨時對象是由表達式'std :: move(bptr)'創建的,臨時對象綁定到右值引用。請注意,一旦創建了此右值引用,「bptr」就會變爲空,因爲它已被移動。 – Nawaz

回答

4

您的案件之間的關鍵區別在於一個事實,即右值引用可以間接地綁定(通過臨時),而非const左值引用不能。在(c)和(d)中,初始化器不相似或可轉換爲[dcl.init.ref]/(5.1)中確定的類型,因此必須應用[dcl.init.ref] /(5.2) - 立即排除(c):

否則,應參考是一個左值參照 非易失const類型(即,CV1應常數),或參考 應一個rvalue參考。也

注意unique_ptr<A>unique_ptr<B>是不同的,不相關的類型,不管如何AB是相關的。

你可以觀察到規則with scalars, too:[不是真的`unique_ptr`特定]

int&& i = 0.f; // Ok 
int& i = 0.f; // Not ok