RVO/NRVO和公共未定义的复制构造函数

Ale*_*tov 9 c++ g++ copy-constructor return-value-optimization

我现在正在与下面的提案作斗争,我想知道合法的,在较小程度上反对它的道德论点.

我们有什么:

#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
}
Run Code Online (Sandbox Code Playgroud)

提出了什么:

#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();
}
Run Code Online (Sandbox Code Playgroud)

这使用最新的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空闲内存并从initvia 返回C*.

谢谢.

Joh*_*itb 10

这使用最新的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); }
Run Code Online (Sandbox Code Playgroud)

现在你可以说

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

int main() {
  C const c(init());
}
Run Code Online (Sandbox Code Playgroud)

  • @Alex 3.2p3,该提案应该被拒绝,因为它不能移植.我理解你的问题,但我想我还会告诉我除了回答你的问题之外我将如何解决它. (4认同)