2013-07-20 80 views
4

我有一個關於我們可以在向量中存儲的類的問題。 什麼是可以存儲在向量中的要求? 看來這樣的類必須有賦值運算符。但我不確定這是否全部。爲什麼std :: vector需要operator =

讓我給你舉個例子。類A有const int成員。如果我不寫operator =,它不會編譯。但在這個例子中,這個運算符什麼都不做。該程序正確顯示10和20。看起來operator =是必需的,但在現實中並未使用。

#include <iostream> 
#include <vector> 

class A { 
public: 
    A(int a) : a_(a) {} 
    A& operator =(const A& a2) { return *this;} // Without this, compile fails. 
    void print() const { 
    std::cerr << a_ << std::endl; 
    } 
private: 
    const int a_; 
}; 

int main(int argc, char** argv) { 
    std::vector<A> v; 
    v.push_back(A(10)); 
    v.push_back(A(20)); 
    for (const A& a : v) a.print(); 
} 
+1

您需要複製構造函數或移動構造函數。 – Rapptz

+0

它編譯VS2012和Gcc 4.8,你使用哪種編譯器? – billz

+0

@Rapptz我實際上發現你需要複製構造函數和複製賦值,或者移動構造函數和移動賦值。 VS2012在內部使用複製/移動構造函數。 GCC似乎想要複製/移動分配。 – Mysticial

回答

4

這可能會讓你大吃一驚:

v.push_back(A(20)); 
v.push_back(A(10)); 
std::sort(begin(v), end(v)); 

有需要可轉讓性,雖然我不知道載體本身的問題,副手,這(我不能編譯代碼告訴,因爲當我刪除operator=()時,我的編譯器不會投訴)。根據Wikipedia(其中引用了'03標準的相關部分),元素必須是CopyConstructibleAssignable

編輯:說回這一天後,似乎額頭,slappingly時std::vector需要Assignable明顯 - 它四處移動元素的任何時間。例如,撥打v.insert()v.erase(),編譯將失敗。

+0

謝謝。我明白它沒有排序。這完全合理。爲什麼push_back失敗? – zorio

+0

@zorio:'push_back()'不會失敗。我只是簡單地表明,在調用'sort()'之前,內容沒有被排序。 –

+0

我用鐺。該行爲看起來依賴於實施。 並且要求是CopyConstructible和Assignable。考慮你的代碼片段,這些要求是有道理的。 謝謝。 – zorio

-1

複製構造函數和賦值運算符的可用性由編譯器保證,如果您不創建自己的。 (默認實現的正確性,雖然是一個不同的故事)。在你的例子中,你幾乎沒有重載A :: opeartor =()。

這裏的問題是不是一個「失蹤」操作符=(),但默認的一個不能用的,因爲你已經宣佈一個const數據成員

const int a_; 

默認的編譯器生成的運營商=()不能賦值給一個const成員。所以,你必須重載你的自我:

A & A::opeartor=(const A & in) 
{ 
    *const_cast<int*>(&a_) = in.a_; // !!!you are expected to do this. 
} 

所以,你的A ::運算符=()版本,什麼也不做,雖然使代碼編譯,簡化版,改變左手操作數的A_值,

+0

1。「operator =()'定義由於被」刪除「而確實丟失。 2.隱式的'operator ='總是「正確的」。這並不意味着做你期望的事情,但這可能是你期望中的一個錯誤,而不是隱式賦值運算符。 3.你不希望做這樣的const_cast東西(!!!)。如果可能的話應該避免。 – Pixelchemist

+0

不知道爲什麼你downvote這個答案。我確實說過,「違約實施的正確性是一個不同的故事」。我沒有做這種const_cast的東西。這是爲了處理用戶在想要對對象執行賦值操作時聲明const成員的原始問題。 – ROTOGG

+0

他實際上不想做任務,他沒有問「如何分配const成員」。他只是想知道爲什麼在向量中存儲他的類時需要定義一個賦值運算符。此外,const_cast賦值不需要編譯器接受賦值運算符。 OP寫了一個,它適合。我懷疑它「不會改變左側操作數的a_值」這一事實在這裏。 – Pixelchemist

2

push_back在向量上會使向量在內存中增長,這意味着 需要通過賦值運算符= 將舊對象複製到新對象,因此您需要賦值運算符=。

+1

這實際上是由複製構造函數完成的,也許在使用保留功能時push_back可能會這樣做(我不確定這種情況),但是當您使用指定數量的元素構建矢量時使用operator =(使用帶有size_t參數的vector構造函數)並使用[]來獲取引用併爲其指定一個值,也可以使用operator = – Zlatomir

+0

+1 for @Zlatomir ok謝謝,那麼我的回答是不正確的。 –

1

如果我不寫operator =,它不會編譯。

令我吃驚,所以我當時一看爲標準,我發現:

你舉的例子有一個隱含缺失的拷貝構造函數,但還是應該編譯如果符合C++ 11標準庫是在手。

在您的示例中對vector使用的類型施加約束的唯一表達式是push_back

一個序列容器類型X<T,A>與分配器Avalue_typeTpush_back()方法,需要T爲:

  • CopyInsertable如果左值或const右值引用被傳遞
  • MoveInsertable如果一個非const的右值被傳遞

這意味着它需要一個有效的拷貝構造函數或(在本例中)一個有效的移動構造函數,它將隱式地從您的代碼中提供。因此,在任何具有有效C++ 11標準庫的編譯器中編譯都不應該失敗。

操作需要的類型,包含在vector可分配:

配套條件

typdef std::vector<T> X; 
X a,b; 
X&& rv; 
X::value_type t; 
X::value_type&& u; 
X::size_type n; 
X::const_iterator p,q; // p = valid for a, q = valid and dereferencable 
initializer_list<T> il; 
[i,j) -> valid iterator-range 

操作

的操作,這需要T可分配的悲觀*名單,如果X是vector,是:

Statement    Requirement on T 

a = b;     CopyInsertable, CopyAssignable 
a = rv;    MoveInsertable, MoveAssignable 
a = il;    CopyAssignable 
a.emplace(p, args); MoveInsertable, MoveAssignable 
a.insert(p, t);  CopyAssignable 
a.insert(p, u);  MoveAssignable 
a.insert(p, n, t);  CopyInsertable, CopyAssignable 
a.insert(p, i, j);  EmplaceConstructible[from *i], MoveInsertable, MoveAssignable 
a.insert(p, il);  -> a.insert(p, il.begin(), il.end()); 
a.erase(q);   MoveAssignable 
a.erase(q1,q2)   MoveAssignable 
a.assign(i,j);   Assignable from *i 
a.assign(il);   -> a.assign(il.begin(), il.end()); 
a.assign(n,t)   CopyAssignable 

* =悲觀意味着可能存在某些條件以實現某些要求。如果您使用上面列出的表達式之一,您的類型T可能需要可分配。

相關問題