RVO/NRVO and public undefined copy constructor - c++

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:
I'm aware of "The Rule of Three" and have read through 12.8/15 and 12.2 of Holy Standard;
I can use neither vector<shared_ptr<T> > nor ptr_vector<T>;
I cannot allocate C in free memory and return it from init via C*.
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; 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());
}

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). 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?
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

Related

Returning reference to a const pointer to const type

Can you look at this example code:
class Test {
int *a;
int b;
public:
Test() : b(2)
{
a = new int(5);
}
const int * const& GetA() const
{
const int * const& c = a;
return a;
}
const int& GetB()
{
return b;
}
~Test()
{
delete a;
}
};
And I get a warning on return a. Why is it wrong to return a reference to a const pointer to a const variable, but it's fine to return a reference to a const variable? By the way if I return c in GetA() it compiles just fine.
Consider first const int& GetB(). That's a neat way of returning a reference to the class member b that can't be modified at the call site. You may as well mark that function const since you are not changing any of the class member data. This is idiomatic C++, especially for types larger than an int, e.g. std::string.
When you write return a;, you are permitting the function call site to modify that class member through that pointer. Although the C++ standard allows this, it circumvents encapsulation and your friendly compiler is warning you of that. Note that since the function itself is not changing the class member, compilation passes despite it being marked as const.
In writing const int * const& c = a; the compiler assumes you know what you're doing.
(As a final note, all havoc is let loose if you attempt to copy an instance of Test due to the compiler generated copy constructor shallow-copying a. You ought to delete the copy constructor and the assignment operators.)

C++ Unwanted implicit conversion in constructor

I have a simple situation where I have some uniform interface, such as this:
class I {
public:
virtual void Run() = 0;
};
I have some templates that have the same interface but do not declare it virtual, for performance reasons. So I need to add one more layer that makes their function virtual:
template <class B>
class S : public I {
B mb;
public:
S(B b)
:mb(b)
{}
virtual void Run()
{
std::cout << mb << std::endl; // or mb.Run()
}
};
Note that I replaced mb.Run() by printing its value. That is only for simplification, it does not affect the behavior I'm encountering here. But anyway, so far so good. Now to make things convenient, I have one more class that makes the interface automatically:
class A {
I *mi;
public:
A()
:mi(0)
{}
template <class B>
A(B b)
:mi(new S<B>(b))
{}
A(A &a)
:mi(a.mi)
{
a.mi = 0;
}
template <class B>
A &operator =(B b)
{
delete mi;
mi = new S<B>(b);
return *this;
}
A &operator =(A &a)
{
delete mi;
mi = a.mi;
a.mi = 0;
return *this;
}
I *operator ->()
{
assert(mi);
return mi;
}
};
This acts as an automatic constructor of S and also as a simple managed pointer at the same time. I would use it as follows:
A a = instanceOfB();
That should call the template A::A<instanceOfB>() which in turn allocates a new S<instanceOfB> on heap and stores it in an A. Now I can call A->Run() which finally resolves to S->Run() and that would call instanceOfB::Run(), which is not virtual.
Instead what happens is that instanceOfB() gets first converted to A and then it dies, as there is no constructor that would take A (only A&). Note that this only happens in g++, Visual Studio 2008 and Visual C++ 6.0 both compile the code without problems. You can reproduce the behavior using:
void Test()
{
A a = 4; // error: no matching function for call to "A::A(A)"
//A a; a = 4; // works
//A a(4); // works
a->Run();
}
I have tried declaring the constructors as explicit, but that doesn't seem to help or I may be doing it wrong. If A was not managing the pointer, I could take value of const A& in constructor, so the whole problem would be solved. Is there another solution to this problem? C++11 is unfortunately NOT available.
I'm trying to implement efficient delegates. Basically I want to be able to do:
int myFunction(int, float);
StaticCallCtx<int, MakeTypelist(int, float)> ctx = Grab(&myFunction)(1, 2.3f);
// ctx.Run() calls the function, with the specified arguments
// it's *not* virtual (compiles to just a few instructions so I
// don't want to spoil it by adding a vfptr)
AutoCallPointer p = ctx;
// or directly AutoCallPointer p = Grab(&myFunction)(1, 2.3f);
// wraps StaticCallCtx, has ->Run() as well, this time there
// is the price of calling the virtual function
Ultimately, high performance (this will later serve for acceleration of some linear algebra functions) and user comfort (short AutoCallPointer p = Grab(fun)(parms) without writing template argument lists) are the main goals here.
EDIT:
The solution of #ecatmur is correct. As it is quite short, I will attempt to reiterate here. g++ correctly refuses to compile the code, as in A there is no copy-constructor that will take A (only A&). The template constructor will not be used in the case of copy initialization A a = instanceOfB().
We must provide a copy-constructor, taking const A&. Because of copy elision, a declaration of the constructor without a body is sufficient. This is however not a nice workarround.
It is better to declare the A::mi as mutable and change the existing A& constructor to take const A& (the copy-operator may also be changed). The fixed A looks like this:
class A {
mutable I *mi;
public:
A()
:mi(0)
{}
template <class B>
A(B b)
:mi(new S<B>(b))
{}
A(const A &a)
:mi(a.mi)
{
a.mi = 0;
}
template <class B>
A &operator =(B b)
{
delete mi;
mi = new S<B>(b);
return *this;
}
A &operator =(const A &a)
{
delete mi;
mi = a.mi;
a.mi = 0;
return *this;
}
I *operator ->()
{
assert(mi);
return mi;
}
};
This code compiles in both g++ and Microsoft's compilers (also in http://codepad.org/9FqUk0Fj).
When you copy-initialize an object of class type, the copy constructor needs to be available, even if the copy is elided. g++ is correct to reject your program; your older versions of MSVC are incorrect to accept it.
You may be able to provide a declaration of a copy constructor without a definition, on the basis that calls to it will be elided or otherwise fail at link time. This could be somewhat confusing, though.
The most obvious solution is to use direct-initialization, which as you have already observed works fine.

C++: how to choose the constructor depending on the condition?

Assume I have a class with different constructors:
class A
{
public:
A(char* string)
{
//...
}
A(int value)
{
//..
}
void check() {}
};
Now I want to create an A object on stack, the constructor must be choosed depending on some condition, but there is a problem: the created object is destroyed then we quit {...} block.
bool isTrue() { /*...*/ }
int main()
{
if (isTrue())
{
A a("string");
}
else
{
A a(10);
}
a.check(); //error: 'a' is not defined in this scope
}
Suppose I haven't the copy-constructor or operator= in the A class. So how can solve this issue?
http://ideone.com/YsjmnK
A a = isTrue() ? A("string") : A(10);
And if a.check() is a const member function, an alternative may be better:
const A& a = isTrue() ? A("string") : A(10);
The object will be destroyed when the reference a go out of scope.
Note since C++17, according to the rule of copy elision the copy/move constructor is not required to be accessible for this case; copy elision is guaranteed here.
And since C++17 you can use std::optional, which doesn't cause any dynamic memory allocation. e.g.
std::optional<A> a;
if (isTrue())
{
a.emplace("string");
}
else
{
a.emplace(10);
}
(*a).check();
BTW: A(char* string) is supposed to be A(const char* string).
You can't satisfy all your stated requirements.
If you can get rid of the requirement for the object to be on stack, you could use a pointer.
A *a;
if (isTrue())
a = new A("string");
else
a = new A(10);
a->check();
delete a;
If the type has a default constructor, you can default-construct an object, immediately destruct it, and then construct it again with the appropriate constructor via placement-new:
A a;
a.~A();
if (isTrue())
{
new(&a) A("string");
}
else
{
new(&a) A(10);
}
The C++ standard has several examples similar to the above, just search for .~ and ->~.
Note that this is ultra evil. If your code ever gets reviewed, you are probably going to get fired.
I had the exact same question a while ago and this is what google helped me find:
unique_ptr<A> foo;
if(isTrue())
foo = std::unique_ptr<A>(new A("10"));
else
foo = std::unique_ptr<A>(new A(10));
Its probably too late for the OP but someone else might hopefully find this useful.
You can use the template class:
template<class type> class A
{
protected:
type T;
public:
void A(type t_curr) {T = t_curr;};//e.g.---
void check() {}
};

Adding const correctness

I have some code which has been written without regard for const correctness. Are there any circumstances in which changing this
class X
{
public:
X(X& rhs); // does not modify rhs
...
};
to this
class X
{
public:
X(const X& rhs);
...
};
would change the behaviour of an existing program? I know this change will allow code which doesn't currently compile to compile, but I'm interested if there is any circumstance in which code that already compiles would change it's behaviour.
Similar question, is there any merit in making this change instead?
class X
{
public:
X(X& rhs); // does not modify rhs
X(const X& rhs);
...
};
For copy constructor I don't think so. But note that in general, yes, declaration of const can affect which method is called. The only example that comes to mind is with array overloading - see e.g. this question.
In fact a copy constructor should, imho, always take a const reference as its argument.
X(X& rhs) { } // does not modify rhs
This does not allow to copy const objects and hence the following code will not compile.
While non-const objects can serve as const arguments, the other way round is impossible
X const test;
X new_x(test);
I can't imagine why someone should preclude the copy of a const object.
Concerning the changes you want to make:
Does the copy constructor rely on any X member function that is defined non-const?
This will work like a charm but permit copying const objects:
class X
{
private:
int a;
public:
X(X &rhs) { a = rhs.value(); }
int& value (void) { return a; }
};
The next example will not compile since rhs is const but value() is not const.
class X
{
private:
int a;
public:
X(X const &rhs) { a = rhs.value(); }
int& value (void) { return a; }
};
If you want to make your class const correct you'll probably have to examine the whole class.
It should only affect your in-class-implementations. Since I don't know a case where external code should rely on "non-constness" of a class member function.
Except when non-const-references are returned by any public member-functions as in my example.
The following snippet will do as intended.
class X
{
private:
int a;
public:
X(int const &b) : a(b) { }
X(X const &rhs) { a = rhs.value(); }
int const & value (void) const { return a; }
};
But be aware that this will interfere with any code like:
X test(100);
test.value() = 12;
This would work using int& value (void) { return a; } but fails with int const & value (void) const { return a; }.
You could of course provide both to be on the safe side.
Since you are changing the class that has the copy constructor I am assuming you can inspect the copy constructors code. If you can make this change and the copy constructor does not give a compiler error you are probably good. One conor case to consider is what the copy assignment operator does as there is no assurance which will be called especially in optimized code. So also make sure that your copy assignment will work with a const parameter.
djechlin is making an important point in his answer, although somewhat unclear, so I will try to explain better.
The constness of an object or reference affects overload resolution. For instance, if you have an object with a const based overload of a member function, different overloads will be chosen:
struct foo {
void do_stuff();
void do_stuff() const;
};
int main() {
foo f;
const foo& fr = f;
f.do_stuff(); // calls non-const version
fr.do_stuff(); // calls const version
}
Now, if one of the overloads has side-effects the other doesn't, you'd get different behavior after changing the signature, given that (or rather even though) the program compiles fine.

Is this C++ reassignment valid?

Sorry for the basic question, but I'm having trouble finding the right thing to google.
#include <iostream>
#include <string>
using namespace std;
class C {
public:
C(int n) {
x = new int(n);
}
~C( ) {
delete x;
}
int getX() {return *x;}
private:
int* x;
};
void main( ) {
C obj1 = C(3);
obj1 = C(4);
cout << obj1.getX() << endl;
}
It looks like it does the assignment correctly, then calls the destructor on obj1 leaving x with a garbage value rather than a value of 4. If this is valid, why does it do this?
If there is a class C that has a constructor that takes an int, is this code valid?
C obj1(3);
obj1=C(4);
Assuming C has an operator=(C) (which it will by default), the code is valid. What will happen is that in the first line obj1 is constructed with 3 as a the parameter to the constructor. Then on the second line, a temporary C object is constructed with 4 as a parameter and then operator= is invoked on obj1 with that temporary object as a parameter. After that the temporary object will be destructed.
If obj1 is in an invalid state after the assignment (but not before), there likely is a problem with C's operator=.
Update: If x really needs to be a pointer you have three options:
Let the user instead of the destructor decide when the value of x should be deleted by defining a destruction method that the user needs to call explicitly. This will cause memory leaks if the user forgets to do so.
Define operator= so that it will create a copy of the integer instead of a copy of the value. If in your real code you use a pointer to something that's much bigger than an int, this might be too expensive.
Use reference counting to keep track how many instances of C hold a pointer to the same object and delete the object when its count reaches 0.
If C contains a pointer to something, you pretty much always need to implement operator=. In your case it would have this signature
class C
{
public:
void operator=(const C& rhs)
{
// For each member in rhs, copy it to ourselves
}
// Your other member variables and methods go here...
};
I do not know enough deep, subtle C++ to explain the problem you are encountering. I do know, however, that it's a lot easier to make sure a class behaves the way you expect if you follow the Rule of Three, which the code you posted violates. Basically, it states that if you define any of the following you should define all three:
Destructor
Copy constructor
Assignment operator
Note as well that the assignment operator implementation needs to correctly handle the case where an object is assigned to itself (so-called "self assignment"). The following should work correctly (untested):
#include <iostream>
#include <string>
using namespace std;
class C {
public:
C(int n) {
x = new int(n);
}
C(const C &other): C(other.getX()) { }
~C( ) {
delete x;
}
void operator=(const C &other) {
// Just assign over x. You could reallocate if you first test
// that x != other.x (the pointers, not contents). The test is
// needed to make sure the code is self-assignment-safe.
*x = *(other.x);
}
int getX() {return *x;}
private:
int* x;
};
void main( ) {
C obj1 = C(3);
obj1 = C(4);
cout << obj1.getX() << endl;
}
Basically you are trying to re-implement a smart pointer.
This is not trivial to get correct for all situations.
Please look at the available smart pointers in the standard first.
A basic implementation (Which will fail under certain situations (copy one of the standard ones to get a better one)). But this should cover the basics:
class X
{
int* data;
public:
// Destructor obvious
~X()
{
delete data;
}
// Easy constructor.
X(int x)
:data(new int(x))
{}
// Copy constructor.
// Relatively obvious just do the same as the normal construcor.
// Get the value from the rhs (copy). Note A class is a friend of
// itself and thus you can access the private members of copy without
// having to use any accessor functions like getX()
X(X const& copy)
:data(new int(copy.x))
{}
// Assignment operator
// This is an example of the copy and swap idiom. This is probably overkill
// for this trivial example but provided here to show how it is used.
X& operator=(X const& copy)
{
X tmp(copy);
this->swap(tmp);
return this;
}
// Write a swap() operator.
// Mark it is as no-throw.
void swap(X& rhs) throws()
{
std::swap(data,rhs.data);
}
};
NEW:
What's happening is that your destructor has deallocated the memory allocated by the constructor of C(4). So the pointer you have copied over from C(4) is a dangling pointer i.e. it still points to the memory location of the deallocated memory
class C {
public:
C(int n) {
x = new int(n);
}
~C( ) {
//delete x; //Don't deallocate
}
void DeallocateX()
{
delete x;
}
int getX() {return *x;}
private:
int* x;
};
int main(int argc, char* argv[])
{
// Init with C(3)
C obj1 = C(3);
// Deallocate C(3)
obj1.DeallocateX();
// Allocate memory and store 4 with C(4) and pass the pointer over to obj1
obj1 = C(4);
// Use the value
cout << obj1.getX() << endl;
// Cleanup
obj1.DeallocateX();
return 0;
}
Be explicit about ownership of pointers! auto_ptr is great for this. Also, when creating a local don't do C obj1 = C(3) that creates two instances of C and initializes the first with the copy constructor of the second.
Heed The Guru.
class C {
public:
C(int n) : x(new int(n)) { }
int getX(){ return *x; }
C(const C& other) : x(new int(*other.x)){}
C& operator=(const C& other) { *x = *other.x; return *this; }
private:
std::auto_ptr<int> x;
};
int main() {
C obj1(3);
obj1 = C(4);
std::cout << obj1.getX() << std::endl;
}
When are you testing the value of obj1? Is it after you leave the scope?
In your example, obj1 is a stack object. That means as soon as you leave the function in which it defined, it gets cleaned up (the destructor is called). Try allocating the object on the heap:
C *obj1 = new C(3);
delete obj1;
obj1 = new C(4);