2011-04-05 69 views
9

我現在正在與以下提案作鬥爭,並且我想知道法律以及在較小程度上反對它的道德論點。RVO/NRVO和公開的未定義拷貝構造函數

我們有什麼:

#include <vector> 

class T; 

class C 
{ 
public: 
    C() { } 
    ~C() { /*something non-trivial: say, calls delete for all elements in v*/ } 
    // a lot of member functions that modify C 
    // a lot of member functions that don't modify C 
private: 
    C(C const &); 
    C& operator=(C const&); 
private: 
    std::vector< T* > v; 
}; 

void init(C& c) { } // cannot be moved inside C 

// ... 
int main() 
{ 
    // bad: two-phase initialization exposed to the clients 
    C c; 
    init(c); 

    // bad: here follows a lot of code that only wants read-only access to c 
    //  but c cannot be declared const 
} 

什麼已經提出:

#include <vector> 

class T; 

class C 
{ 
public: 
    C() { } 
    ~C() { /*calls delete for all elements in v*/ } 

    // MADE PUBLIC 
    C(C const &); // <-- NOT DEFINED 

    // a lot of member functions that modify C 
    // a lot of member functions that don't modify C 
private: 
    C& operator=(C const&); 
private: 
    vector< T* > v; 
}; 

C init() // for whatever reason object CANNOT be allocated in free memory 
{ 
    C c; 
    // init c 
    return c; 
} 

// ... 
int main() 
{ 
    C const & c = init(); 
} 

這編譯並使用最新G ++(這是唯一的目標編譯器)鏈接(和作品)都4.1.2和4.4.5-由於(N)RVO,複製構造函數永遠不會被調用;僅在main()的末尾調用析構函數。

聲稱該技術是完全正確的,因爲沒有辦法複製構造函數可能被誤用(如果它曾經被生成,它將是鏈接器錯誤),並使其公開,防止編譯器抱怨私人的。

對於我來說,使用這樣的技巧看起來確實是錯誤的,我認爲這與C++精神相矛盾,並且看起來更像是破解 - 這個詞的意義不大。

我的感受並不充分,所以我現在在尋找技術。

請不要張貼教科書C++的東西在這裏:

  • 我所知道的「三個規律」,並通過12.8/15和神聖的標準的12.2已經閱讀;
  • 我既不能使用vector<shared_ptr<T> >也不能使用ptr_vector<T>;
  • 我無法在可用內存中分配C,並通過C*init返回。

謝謝。

+1

我假設移動施工不適合你? – 2011-04-05 14:15:01

+0

@Konrad Rudolph:不幸的是(g ++ 4.1.2是我們應該支持的編譯器之一,因此沒有0x功能)。 – 2011-04-05 14:17:25

+5

「使其公開可以防止愚蠢的編譯器抱怨私人。」 - 不是愚蠢的編譯器。該標準要求拷貝構造函數即使在拷貝被刪除時也是可訪問的,因爲拷貝elision是可選的。因此,如果它被允許是私密的,那麼依賴elision來避免錯誤的程序無論如何都不會嚴格符合,在這種情況下,標準要求被診斷。編譯器正在幫你編寫可移植的代碼,誠然違揹你的願望;-) – 2011-04-05 14:23:09

回答

10

這編譯和鏈接(和作品)最近使用G ++(這是唯一的目標編譯器)都4.1.2和4.4.5 - 因爲(N),視網膜靜脈阻塞的拷貝構造函數是永遠所謂的;僅在main()的末尾調用析構函數。

雖然它可能適用於GCC,但您的代碼確實具有未定義的行爲,因爲它引用了未定義的函數。在這種情況下,你的程序是不合格的;不需要診斷。這意味着GCC可能會忽略規則違規,但其他編譯器可能會診斷它或執行其他奇怪的操作。

所以在這些理由,我會拒絕這種方式。

我的感受並不充分,所以我現在在尋找技術。

你想在這裏有移動語義。如何明確這一點?

class T; 
class C; 

struct CMover { 
    C *c; 
private: 
    CMover(C *c):c(c) { } 
    friend CMover move(C &c); 
}; 

class C { 
public: 
    C() { } 
    ~C() { /*calls delete for all elements in v*/ } 

    C(CMover cmove) { 
     swap(v, cmove.c->v); 
    } 

    inline operator CMover(); 

    // a lot of member functions that modify C 
    // a lot of member functions that don't modify C 
private: 
    C& operator=(C const&); // not copy assignable 
    C(C &); // not lvalue copy-constructible 

private: 
    vector< T* > v; 
}; 

CMover move(C &c) { return CMover(&c); } 
C::operator CMover() { return move(*this); } 

現在你可以說

C init() // for whatever reason object CANNOT be allocated in free memory 
{ 
    C c; 
    return move(c); 
} 

int main() { 
    C const c(init()); 
} 
+0

你能指點我哪裏C++ 03禁止引用未定義的函數?關於移動語義,我們不希望它在這種特殊情況下非常糟糕:我的問題更多的是關於爲什麼應該接受或禁止此提議。 – 2011-04-05 14:52:04

+4

@Alex 3.2p3,建議應該被拒絕,因爲它不能移植工作。我理解你的問題,但我想我還會告訴我除了回答你的問題之外,我會如何解決它。 – 2011-04-05 15:03:21

+0

謝謝,3.2/2 + 3.2/3清楚地表明那是UB。此外,我們剛剛發現MSVC++ 2008和2010(現在不支持*,但您永遠不知道)會拒絕此代碼。 – 2011-04-06 08:48:36

0

顯而易見的答案是,編譯器沒有義務刪除拷貝構造,特別是如果禁用了優化(儘管g ++仍然優化拷貝,即使沒有優化)。因此,你限制了可移植性。

最有可能的是,如果它在特定的編譯器上編譯,它將按預期工作。

我知道你可以做什麼看起來有很多看似隨意/人爲的限制,但是你能夠使用C的代理持有人嗎?

class C 
{ 
private: 
    C() { } 
    ~C() { /*calls delete for all elements in v*/ } 

    // ... 
    friend class C_Proxy; 
}; 

class C_Proxy 
{ 
public: 
    C_Proxy() : c_() { init(c_); } 

    // Or create a public const& attribute to point to this. 
    const C& get_C() const { return c_; } 

private: 
    C c_; 
}; 
0

這看起來像是你不會離開人民頭上的技術上的原因單獨(又名「但它編譯和作品對我們的編譯器!」),所以也許在概念上更簡單的方法可能是一個好主意?

如果您關注的是常量性...

C c; 
init(c); 

// bad: here follows a lot of code that only wants read-only access to c 
//  but c cannot be declared const 

C const & c = init(); 

最簡單的(如:不需要黑客和代理的東西)解決辦法可能是:

C c_mutable_object; 
init(c_mutable_object); 
C const& c = c_mutable_object; 

// ... lots of code with read-only access to c