C++ polymorphism with boost scoped_ptr - c++

Why does the following code not allow foo(ptr) to be called ?
#include <boost/scoped_ptr.hpp>
struct A {
virtual ~A() {}
};
struct B: public A {};
void foo(boost::scoped_ptr<A>& a) {}
void goo(A& a) {}
int main() {
boost::scoped_ptr<B> ptr(new B);
foo(ptr);
B b;
goo(b);
}
The corresponding form where we pass references works as expected. Are we supposed not to do polymorphism
with boost scoped_ptr ?
g++ with boost 1.49 gives me:
error: invalid initialization of reference of type ‘boost::scoped_ptr<A>&’ from expression of type ‘boost::scoped_ptr<B>’

That's because foo, for some reason, takes a scoped pointer by reference. That is completely unnecessary and is the reason why the call fails. There is a conversion from scoped_ptr<B> to scoped_ptr<A> but not from scoped_ptr<B>& to scoped_ptr<A>&.
You should pass it as reference to const.
void foo(boost::scoped_ptr<A> const & a) {}
Incidentally, this isn't a "problem" of smart pointers per se. The following code fails for the same reasons as yours.
void foo(A*& p) {}
int main()
{
B* p = new B;
foo(p); //FAIL
}
In order to fix this you have to pass the pointer either by value, or, if you're sufficiently perverted, by reference to const
void foo (A * const & p); // <-- a perv wrote this

Related

Const ref versus const pointer as a data member for operator= overloading

A class with a 'const int&' data member causes the following compilation error when overloading operator= (compiler is g++):
assignment of read-only location.
When the data member is changed to 'const int*', it is fine.
Why?
#include <iostream>
using namespace std;
class B {
private:
const int& ir;
//const int* ir;
public:
B(const int& i) : ir(i) {}
//B(const int& i) : ir(&i) {}
B& operator=(B& b) { ir = b.ir; return *this; }
};
g++ error message:
opertostack.cpp:9:31: error: assignment of read-only location ‘((B*)this)-> B::ir’
B& operator=(B& b) { ir = b.ir; return *this; }
^~
You cannot rebind references. Once they are created, they need to refer to the same object for their entire lifetime.
However, your code has much bigger issues. Let's just remove operator=() so it compiles. We then instantiate a B:
B b(42);
b.ir was bound to the temporary int passed to the constructor. That temporary doesn't exists anymore after the constructor returns. b.ir is now a dangling reference. It refers to an object that does not exist anymore.
A pointer isn't going to help you either. If we change B::ir to be a const int* and switch the commented out code, then the result of instantiating a B as above is now a dangling pointer. It points to a temporary that doesn't exist anymore.
So in both cases, you get undefined behavior when using B::ir.
What you want here is just a normal int member. The constructor parameter doesn't need to be a reference either in this case. An int is just as easy to copy as a reference so you don't gain anything by using a reference parameter. Finally, the assignment operator should take a const reference, so that you can assign from const B objects as well:
class B {
private:
int ir;
public:
B(const int& i) : ir(i) {}
B& operator=(const B& b) { ir = b.ir; return *this; }
};

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

Passing a pointer to temporary object

We know that we can pass temporary objects to functions by const reference, like this:
class A
{
public:
A(int _b = 0)
{
b = _b;
}
int b;
};
void foo(A& a) {printf("%d", a.b);}
void cfoo(const A& a) {printf("%d", a.b);}
int main(void)
{
//foo(A(4)); doesn't compile
cfoo(A(5));
}
but what about passing by pointer?
why does this compile?
void pfoo(A* pa) {pa->b = 19;}
int main(void)
{
pfoo(&A(5));
}
but what about passing anonymous variables pointer? why does this compile?
You are probably using a compiler that does not honour C++ standard.
No address of an r-value (temporary) object can be taken. That should not compile.
However, operator& can be overloaded, so that it can be invoked on a temporary object, e.g.:
struct A
{
A* operator&() { return this; }
};
In C++11 a temporary object can be bound to an r-value reference. After that r-value reference behaves like an l-value and hence the address of a temporary object can be taken:
struct A {};
void foo(A*);
void foo(A&& a) { foo(&a); }
int main() {
foo(A{});
}

C++ template member function having pointer reference template parameter

class BI {
public:
virtual void fun() = 0;
virtual ~BI() {}
};
class B : public BI {
public:
void fun() {}
};
template <typename T>
class A {
T* obj;
public:
void funT(const T*&) /* adding reference is creating error */;
};
template <typename T>
void A<T>::funT(const T*& obj) {
}
int main() {
A<B> obj;
obj.funT(new B());
}
when compiling the above code with g++ compiler, I am getting the error: no matching function for call to A::funT(B)*. But when I remove the reference '&' operator from the funT() declaration as void funT(const T* obj), then it compiles and works fine. Why the reference operator is not allowed here?
You're asking for a reference to a pointer, you can't get references (unless they're C++11 rvalue references) to temporary values.
Make sure you have a lvalue passed as parameter to have the reference working.
e.g.
#include <iostream>
using namespace std;
class BI {
public:
virtual void fun() = 0;
virtual ~BI() {}
};
class B : public BI {
public:
void fun() {}
};
template <typename T>
class A {
T* obj;
public:
void funT(const T*&);
};
template <typename T>
void A<T>::funT(const T*& obj) {
}
int main() {
A<B> obj;
const B* ptr = new B(); // <-- This is an lvalue
obj.funT(ptr);
delete ptr; // Also clean it up after you used it
}
http://ideone.com/T4QJzi
Here is a more simple program which exhibits the same problem:
void fun(const int*&) {}
int main() {
int x;
fun(&x);
}
It yields the following error:
invalid initialization of non-const reference of type ‘const int*&’
from an rvalue of type ‘int*’
That makes sense. fun takes an argument of type "reference to non-const pointer to const int", and we tried to pass it a temporary of type "pointer to int". References to non-const types don't bind to temporaries, because temporaries are usually immutable. In this case, if we were allowed to pass &x as the argument to fun, then fun would be able to modify the address of x, which doesn't make any sense.
As you noticed, removing the & makes the code well formed:
void fun(const int*) {}
Now we are simply passing a pointer to int where a value of type pointer to const int is expected, which is a simple implicit conversion.
Alternatively, you might have intended for fun to take an argument of type "reference to const pointer to int":
void fun(int* const&) {}
Or a reference to const pointer to const int:
void fun(const int* const&) {}
But a reference to a const pointer is kind of silly, as a simple pointer would be just as good.
Finally, you could keep your original declaration of fun, and just avoid trying to pass a temporary as its argument.
The error msg is clear. Parameter type is of reference to pointer to T, but you are sending pointer to T. Temporaries cannot be passed as reference in this case. You can write:
int main() {
A<B> obj;
const B* b=new B(); //create a lvalue
obj.funT(b);
delete b; // make sure to release memory.
}

Union of const/non-const Object Pointers

Consider the sample code below:
class A
{
public:
A() : n(0) {}
int n;
};
class B
{
public:
B(A* a) : mA(a) { }
B(const A* a) : mConstA(a) { }
union {
A* mA;
const A* mConstA;
};
};
int main()
{
A a;
B b1(&a);
B b2(const_cast<const A*>(&a));
return 0;
}
At construction, b1 would have a mutable pointer to a, while b2 would have an immutable pointer to a. In this scenario, b1.mA equals b2.mConstA, and b1.mConstA equals b2.mA.
Are these equalities always true when you have a union of const and non-const object pointers?
Similarly, the code below compiles/runs fine:
int main()
{
const A a;
B b(&a);
b.mA->n = 3; // Blasphemy!!!
return 0;
}
But is it guaranteed for b.mA to always be equal to b.mConstA?
Are these equalities always true when you have a union of const and non-const members?
Yes, both pointers will refer to the same object in the same address. The bits in memory will be the same.
But is it guaranteed for b.mA to always be equal to b.mConstA?
Yes, their values will be the same, but that does not mean that you can really use it. This is equivalent to using const_cast, you will get a non-const pointer to the object, but if the object is really const, using that pointer to modify the object is undefined behavior.