簡體   English   中英

RVO / NRVO和公共未定義的復制構造函數

[英]RVO/NRVO and public undefined copy constructor

我現在正在與下面的提案作斗爭,我想知道合法的,在較小程度上反對它的道德論點。

我們有什么:

#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返回它。

謝謝。

這使用最新的g ++(這是唯一的目標編譯器)編譯和鏈接(並且工作)4.1.2和4.4.5 - 因為(N)RVO,從不調用復制構造函數; 析構函數僅在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());
}

顯而易見的答案是編譯器沒有義務忽略復制結構,特別是如果禁用優化(盡管即使沒有優化,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_;
};

這看起來像是你不會僅僅因為技術原因而從人們頭腦中得到的東西(又名“但它編譯並適用於我們的編譯器!”),所以也許概念上更簡單的方法可能是一個好主意?

如果你關心的是常量......

 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

暫無
暫無

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

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