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; destructor is called at the end of main() only.
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.
My feelings is not sufficient argumentation, so I'm looking for technicalities now.
Please don't post textbook C++ stuff here:
vector<shared_ptr<T> >
nor ptr_vector<T>
;C
in free memory and return it from init
via C*
.Thank you.
Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.
Bright implemented this optimization in his Zortech C++ compiler. This particular technique was later coined "Named return value optimization" (NRVO), referring to the fact that the copying of a named object is elided.
A copy constructor is a member function that initializes an object using another object of the same class. In simple terms, a constructor which creates an object by initializing it with an object of the same class, which has been created previously is known as a copy constructor.
Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).
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; destructor is called at the end of main() only.
While it may work with GCC, your code really has undefined behavior because it references a function that's not defined. 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.
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());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With