简体   繁体   English

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

[英]RVO/NRVO and public undefined copy constructor

I'm fighting the following proposal now, and I want to know legal and for lesser extent moral arguments against it or for it. 我现在正在与下面的提案作斗争,我想知道合法的,在较小程度上反对它的道德论点。

What we had: 我们有什么:

#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
}

What has been proposed: 提出了什么:

#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();
}

This compiles and links (and works) using recent g++ (which is the only target compiler) both 4.1.2 and 4.4.5 -- because of (N)RVO the copy-constructor is never called; 这使用最新的g ++(这是唯一的目标编译器)编译和链接(并且工作)4.1.2和4.4.5 - 因为(N)RVO,从不调用复制构造函数; destructor is called at the end of main() only. 析构函数仅在main()的末尾调用。

It is claimed that the technique is perfectly fine, because there is no way copy-constructor could be mis-used (if it ever have been generated it would be linker error), and making it public prevents compiler for complaining about private one. 声称该技术非常好,因为复制构造函数无法被误用(如果它曾经生成它将是链接器错误),并且公开它会阻止编译器抱怨私有。

It looks really-really wrong for me to use such trick, which I feel contradicts the C++ spirit and looks more like hack -- in the bad sense of the word. 对我来说,使用这样的技巧看起来真的非常错误,我觉得这与C ++精神相矛盾,而且看起来更像是黑客 - 在这个词的不好意义上。

My feelings is not sufficient argumentation, so I'm looking for technicalities now. 我的感受并不充分,所以我现在正在寻找技术细节。

Please don't post textbook C++ stuff here: 请不要在这里发布教科书C ++的东西:

  • I'm aware of "The Rule of Three" and have read through 12.8/15 and 12.2 of Holy Standard; 我知道“三法则”,并阅读了神圣标准的12.8 / 15和12.2;
  • I can use neither vector<shared_ptr<T> > nor ptr_vector<T> ; 我既不能使用vector<shared_ptr<T> >也不能使用ptr_vector<T> ;
  • I cannot allocate C in free memory and return it from init via C* . 我无法在空闲内存中分配C并通过C*init返回它。

Thank you. 谢谢。

This compiles and links (and works) using recent g++ (which is the only target compiler) both 4.1.2 and 4.4.5 -- because of (N)RVO the copy-constructor is never called; 这使用最新的g ++(这是唯一的目标编译器)编译和链接(并且工作)4.1.2和4.4.5 - 因为(N)RVO,从不调用复制构造函数; destructor is called at the end of main() only. 析构函数仅在main()的末尾调用。

While it may work with GCC, your code really has undefined behavior because it references a function that's not defined. 虽然它可能适用于GCC,但您的代码确实具有未定义的行为,因为它引用了未定义的函数。 In such a case, your program is ill-formed; 在这种情况下,你的程序是不正确的; no diagnostic required. 无需诊断。 Which means that GCC may ignore the rule violation, but other compilers may diagnose it or do something else strange. 这意味着GCC可能会忽略规则违规,但其他编译器可能会诊断它或做其他奇怪的事情。

So on those grounds, I would reject this way. 所以基于这些理由,我会拒绝这种方式。

My feelings is not sufficient argumentation, so I'm looking for technicalities now. 我的感受并不充分,所以我现在正在寻找技术细节。

You want to have move semantics here. 你想在这里移动语义。 What about having this explicit? 有这个明确的呢?

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); }

Now you can say 现在你可以说

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

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

The obvious answer is that the compiler is under no obligation to elide the copy construction, especially if optimization is disabled (although g++ still elides the copy even without optimization). 显而易见的答案是编译器没有义务忽略复制结构,特别是如果禁用优化(尽管即使没有优化,g ++仍然会使副本省略)。 Thus, you're restricting portability. 因此,您限制了可移植性。

Most likely if it compiles on a particular compiler it would function as expected though. 最有可能的是,如果它在特定编译器上编译,它将按预期运行。

I know there are a ton of seemingly arbitrary/artificial restrictions on what you can do, but are you able to use a proxy holder for C ? 我知道你可以做的事情有很多看似随意/人为的限制,但你能用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_;
};

This looks like something you wont get out of peoples heads on technical reasons alone (aka "But it compiles and works on our compiler!"), so maybe a conceptually simpler approach might be a good idea? 这看起来像是你不会仅仅因为技术原因而从人们头脑中得到的东西(又名“但它编译并适用于我们的编译器!”),所以也许概念上更简单的方法可能是一个好主意?

If your concern is the constness ... 如果你关心的是常量......

 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 

vs.

 C const & c = init(); 

the simplest (as in: no hacks and proxy stuff required) solution might be: 最简单的(如:无需黑客和代理服务器)解决方案可能是:

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