no match for ‘operator=’ - c++

In the following example, I am trying if I try to return by reference from fun() then it gives me warning as it should.
However when I try to return by value, I am expecting 'operator=()' to get triggered, but it is throwing the following error which I am not able to figure out why:
error: no match for ‘operator=’ (operand types are ‘Auto_ptr2<Resource>’ and ‘Auto_ptr2<Resource>’)
note: no known conversion for argument 1 from ‘Auto_ptr2<Resource>’ to
‘Auto_ptr2<Resource>&’
Surprisingly, res2=res1 invoked 'operator=()' just fine!!!
template<class T>
class Auto_ptr2
{
T* m_ptr;
public:
Auto_ptr2(T* ptr=nullptr)
:m_ptr(ptr)
{std::cout<<"\nAuto_ptr CTOR called ";}
~Auto_ptr2()
{
std::cout<<"\n~Auto_ptr2 called\n";
delete m_ptr;
}
// A copy constructor that implements move semantics
Auto_ptr2(Auto_ptr2&
{
std::cout<<"\nAuto_PTR2 copy constructor called";
m_ptr = a.m_ptr;
a.m_ptr = nullptr;
}
Auto_ptr2& operator=(Auto_ptr2& a)
{
std::cout<<"\nAuto_ptr2 operator = called";
if (&a == this)
return *this;
delete m_ptr;
m_ptr = a.m_ptr;
a.m_ptr = nullptr;
return *this;
}
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
bool isNull() const { return m_ptr == nullptr; }
};
class Resource
{
public:
Resource() { std::cout << "\nResource acquired"; }
~Resource() { std::cout << "\nResource destroyed"; }
};
Auto_ptr2<Resource> fun(Auto_ptr2<Resource> res3)
{
std::cout << "\nres1 is " << (res3.isNull() ? "null" : "not null");
return res3;
}
int main()
{
Auto_ptr2<Resource> res1(new Resource);
Auto_ptr2<Resource> res2;
res2 = res1; // res2 assumes ownership, res1 is set to null
res2=fun(res2); //ERROR : error: no match for ‘operator=’ (operand types are ‘Auto_ptr2<Resource>’ and ‘Auto_ptr2<Resource>’)
return 0;
}

If I understand correctly, you're writing something similar to std::unique_ptr.
So, as for std::unique_ptr, the operator=() should works with move semantics, so receiving a r-vale reference, Auto_ptr &&, not an l-value reference
Auto_ptr2& operator=(Auto_ptr2 && a)
{
std::cout<<"\nAuto_ptr2 operator = called";
if (&a == this)
return *this;
delete m_ptr;
m_ptr = a.m_ptr;
a.m_ptr = nullptr;
return *this;
}
And the use should pass through std::move()
res2=fun(std::move(res2));
Same problem with constructors: avoid copy constructor (maybe delete it) and write a move constructor.
Auto_ptr2 (Auto_ptr2 const &) = delete;
Auto_ptr2 (Auto_ptr2 && a)
{
std::cout<<"\nAuto_PTR2 move constructor called";
m_ptr = a.m_ptr;
a.m_ptr = nullptr;
}

Related

How to improve my custom unique_ptr class? I want to realize that derived class can cast to base class

I realize a unique_ptr class by myself as below, and use it to manage another class:
namespace mcj {
template <typename CallbackT>
class unique_ptr {
public:
unique_ptr(CallbackT* ptr = nullptr) : ptr_(ptr) {
std::cout << "unique_ptr normal constructor" << std::endl;
}
unique_ptr(unique_ptr<CallbackT>&& ptr) {
if (ptr_) {
delete ptr_;
ptr_ = nullptr;
}
ptr_ = ptr.release();
std::cout << "unique_ptr move constructor(unique_ptr<CallbackT>&& ptr)"
<< std::endl;
}
unique_ptr<CallbackT>& operator=(unique_ptr<CallbackT>&& ptr) {
if (ptr_) {
delete ptr_;
ptr_ = nullptr;
}
ptr_ = ptr.release();
std::cout << "unique_ptr move operation(=)" << std::endl;
return *this;
}
unique_ptr(const unique_ptr<CallbackT>& other) = delete;
unique_ptr<CallbackT>& operator=(const unique_ptr<CallbackT>& other) = delete;
~unique_ptr() {
delete ptr_;
ptr_ = nullptr;
}
CallbackT& operator*() { return *ptr_; }
CallbackT* operator->() { return ptr_; }
CallbackT* get() const { return ptr_; };
CallbackT* release() {
if (ptr_) {
CallbackT* temp = ptr_;
ptr_ = nullptr;
return temp;
}
return ptr_;
}
private:
CallbackT* ptr_;
};
} // namespace mcj
I call mcj::unique_ptr like this, A is base base class and B is derived class:
mcj::unique_ptr<A> CastToBase() {
return mcj::unique_ptr<B>(new B);
}
mcj::unique_ptr<A> h = CastToBase();
But an error occurs:
/Users/chaojie.mo/Documents/test/test/main.cpp:42:10: error: no viable
conversion from returned value of type 'unique_ptr' to function
return type 'unique_ptr' return mcj::unique_ptr(new B);
^~~~~~~~~~~~~~~~~~~~~~~~~ /Users/chaojie.mo/Documents/test/src/callback_utils.h:11:3: note:
candidate constructor not viable: no known conversion from
'mcj::unique_ptr' to 'A ' for 1st argument unique_ptr(CallbackT
ptr = nullptr) : ptr_(ptr) { ^
/Users/chaojie.mo/Documents/test/src/callback_utils.h:15:3: note:
candidate constructor not viable: no known conversion from
'mcj::unique_ptr' to 'unique_ptr &&' for 1st argument
unique_ptr(unique_ptr&& ptr) { ^
/Users/chaojie.mo/Documents/test/src/callback_utils.h:35:3: note:
candidate constructor not viable: no known conversion from
'mcj::unique_ptr' to 'const unique_ptr &' for 1st argument
unique_ptr(const unique_ptr& other) = delete;
Can someone tell me how to improve mcj::unique_ptr to support that a derived class can cast to a base class?
foo<B> is not a subclass of foo<B> in c++. Google 'contravariance and covariance in c++ smart pointers',
You will have to extract the pointer cast it and create a new unique_ptr to return

What happens internally when std::move is called?

I wanted to know what exactly happens internally when we call std::move on an object.
For example:
class Holder {
int* m_ptr;
int m_size;
public:
Holder(int size) : m_size(size),
m_ptr(size ? new int[size] : 0) {
cout << "Constructor\n";
}
~Holder() {
cout << "Destructor\n";
delete[] m_ptr;
}
Holder(const Holder& other) {
cout << "Copy Constructor\n";
m_ptr = new int[other.m_size];
std::copy(other.m_ptr, other.m_ptr + other.m_size, m_ptr);
m_size = other.m_size;
}
void swap(Holder& other) noexcept {
std::swap(this->m_ptr, other.m_ptr);
std::swap(this->m_size, other.m_size);
}
Holder& operator=(const Holder& other) {
cout << "Copy Assignment\n";
Holder(other).swap(*this);
}
Holder(Holder&& other) : m_ptr(other.m_ptr), m_size(other.m_size) {
cout << "Move Constructor\n";
other.m_ptr = nullptr;
other.m_size = 0;
}
Holder& operator=(Holder&& that) noexcept {
cout << "Move Assignment\n";
Holder(std::move(that)).swap(*this);
return *this;
}
};
Holder createHolder(int size) {
return Holder(size);
}
int main(void) {
Holder h = createHolder(1000);
return 0;
}
Gives the output as when compiled with '-fno-elide-constructors':
Constructor
Move Constructor
Destructor
Move Constructor
Destructor
Destructor
Since we are returning from a function createHolder and the return object is on the stack, how does the main function still receives it? Not getting exactly what happens internally in memory.
Thanks.
std::move is a noop function that converts any kind of reference into an rvalue reference. So it doesn't actually "do" anything, it just changes how it's argument is used.
template <class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept;
Returns: static_cast<remove_reference_t<T>&&>(t).
std::move make your operator =, constructor is call correctly with what you defined in the Holder class
Holder(Holder&& other) : m_ptr(other.m_ptr), m_size(other.m_size) {
cout << "Move Constructor\n";
other.m_ptr = nullptr;
other.m_size = 0;
}
Holder& operator=(Holder&& that) noexcept {
cout << "Move Assignment\n";
Holder(std::move(that)).swap(*this);
return *this;
}
Since we are returning from a function createHolder and the return object is on the stack, how does the main function still receives it?
because your move constructor
you already copy pointer m_ptr(other.m_ptr) m_size(other.m_size) from
Holder(size) -> move to Holder object in return register on stack -> move to Holder h in main function
Constructor -> of Holder(size);
Move Constructor -> Holder(size) move to Holder object in return register on stack
Destructor -> destructor of Holder(size)
Move Constructor -> from Holder object on stack -> Holder h in main
Destructor -> destructor of Holder object in return register
Destructor -> destructor of Holder h in main

Custom reference counting pointer calls destructor right after creation

I am currently reading the "3D Game Engine Architecture" book by David H. Eberly, and decided to implement my own little reference counting smart pointer. I have mostly followed his implementation, but I am experiencing a problem with my implementation.
I created a function called 'CreateRef' which returns a Pointer. All is well when I use this function in the same scope as the object I have created, but the moment I put the object in the global scope it destroys the object right after creation.
class Object
{
public:
void IncrementReferences()
{
++m_References;
}
void DecrementReferences()
{
if(--m_References == 0) delete this;
}
int GetReferenceCount() const { return m_References; }
private:
int m_References = 0;
};
template<class T>
class Pointer
{
public:
//costr and destr
Pointer(T* pObject = nullptr)
{
m_pObject = pObject;
if (m_pObject)
m_pObject->IncrementReferences();
}
Pointer(const Pointer& rPointer)
{
m_pObject = rPointer.m_pObject;
if (m_pObject)
m_pObject->IncrementReferences();
}
~Pointer()
{
if (m_pObject)
m_pObject->DecrementReferences();
}
// implicit conversions
operator T* () const
{
return m_pObject;
}
T& operator* () const
{
return *m_pObject;
}
T* operator-> () const
{
return m_pObject;
}
// Assignment
Pointer& operator= (T* pObject)
{
if (m_pObject != pObject)
{
if (pObject)
pObject->IncrementReferences();
if (m_pObject)
m_pObject->DecrementReferences();
m_pObject = pObject;
}
return *this;
}
Pointer& operator= (const T* rReference)
{
if (m_pObject != rReference)
{
if (rReference)
rReference->IncrementReferences();
if (m_pObject)
m_pObject->DecrementReferences();
m_pObject = rReference;
}
return *this;
}
// Comparisons
bool operator== (T* pObject) const { return m_pObject == pObject; }
bool operator!= (T* pObject) const { return m_pObject != pObject; }
bool operator== (const Pointer& rReference) const { return m_pObject == rReference.m_pObject; }
bool operator!= (const Pointer& rReference) const { return m_pObject != rReference.m_pObject; }
protected:
// The shared object
T* m_pObject;
};
template<typename T>
using Ref = Pointer<T>;
template<typename T, typename ...Args>
constexpr Ref<T> CreateRef(Args&&... args)
{
return Ref<T>(new T(args...));
}
Main
static Ref<Person> person = nullptr; // Doesn't work like this
static void DoSomething()
{
person = CreateRef<Person>("Name");
std::cout << "References " << person->GetReferenceCount() << std::endl;
Ref<Person> newPerson = person;
std::cout << "References " << newPerson->GetReferenceCount() << std::endl;
}
int main()
{
DoSomething();
std::cout << person->GetReferenceCount();
}
I have a feeling I am doing something wrong with the 'Pointer' class but I can't quite understand what I am missing.
Thanks for the help. I found two solutions to my problem.
First solution is to add a copy assignment operator to the Pointer class.
Pointer& operator= (Pointer& rPointer)
{
if (m_pObject != rPointer.m_pObject)
{
if (rPointer)
rPointer.m_pObject->IncrementReferences();
if (m_pObject)
m_pObject->DecrementReferences();
m_pObject = rPointer.m_pObject;
}
return *this;
}
Another solution (albeit not what I was looking for) was to change the return type of the CreateRef() function to be a T*
template<typename T, typename ...Args>
constexpr T* CreateRef(Args&&... args)
{
return new T(args...);
}

invalid initialization of non-const reference of type from an rvalue of type

I am writing some code based on issue 28 smart pointer of more effective c++ as follows. However, it cannot compile:
main.cpp: In instantiation of 'SmartPointer<T>::operator SmartPointer<U>() [with U = MusicProduct; T = Cassette]':
main.cpp:99:17: required from here
main.cpp:48:39: error: invalid initialization of non-const reference of type 'SmartPointer<MusicProduct>&' from an rvalue of type 'SmartPointer<MusicProduct>'
return SmartPointer<U> (ptr_);
^
main.cpp:16:9: note: initializing argument 1 of 'SmartPointer<T>::SmartPointer(SmartPointer<T>&) [with T = MusicProduct]'
SmartPointer(SmartPointer<T>& other)
^
Either of these two changes works:
in the implementation of operator SmartPointer (), create an object and return:
SmartPointer a(ptr_);
return a;
Or, make the parameter of the copy constructor as const
SmartPointer(const SmartPointer& other)
and comment the line
other.ptr_ = nullptr;
Is there any reason why either of the solutions works? Thanks.
#include <iostream>
template <typename T>
class SmartPointer
{
public:
SmartPointer(T* ptr) : ptr_(ptr) {}
~SmartPointer()
{
if (ptr_)
{
delete ptr_;
}
}
SmartPointer(SmartPointer<T>& other)
{
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
SmartPointer<T>& operator = (SmartPointer<T>& other)
{
if (this == &other)
{
return *this;
}
if (ptr_)
{
delete ptr_;
}
ptr_ = other.ptr_;
other.ptr_ = nullptr;
return *this;
}
template <typename U>
operator SmartPointer<U> ()
{
// it works
//SmartPointer<U> a(ptr_);
//return a;
// error
return SmartPointer<U> (ptr_);
}
T& operator * () const
{
return *ptr_;
}
T* operator -> () const
{
return ptr_;
}
private:
T* ptr_ = nullptr;
};
class MusicProduct
{
public:
MusicProduct(const std::string& name) : name_(name) {}
virtual ~MusicProduct() {}
virtual void Play() const = 0;
virtual void ShowName() const
{
std::cout << name_ << std::endl;
}
private:
std::string name_;
};
class Cassette : public MusicProduct
{
public:
Cassette(const std::string& name) : MusicProduct(name) {}
void Play () const
{
std::cout << "play cassette" << std::endl;
}
};
void CallPlay(const SmartPointer<MusicProduct>& sp)
{
sp->Play();
}
int main()
{
SmartPointer<Cassette> a(new Cassette("Zhang"));
a->ShowName();
CallPlay(a);
return 0;
}
That's because your copy ctor has a non-const reference parameter and therefore cannot accept a temporary. Thus
return SmartPointer<X>(y);
won't work. The argument to the return keyword is a temporary, and it needs to be copied, so here the design breaks down.
Since you are using C++11, you can fix this by introducing a move constructor (and move assignment).
You can also make the argument const and designate the ptr_ member as mutable. This will allow you to copy from temporaries and const smart pointers, but for the price of actually mutating them.

Ref counted smart pointer's assignment operator

despite the ocean of smart pointer questions out there, I seem to be stuck with one more. I am trying to implement a ref counted smart pointer, but when I try it in the following case, the ref count is wrong. The comments are what I think should be the correct ref counts.
Sptr<B> bp1(new B); // obj1: ref count = 1
Sptr<B> bp2 = bp1; // obj1: ref count = 2
bp2 = new B; // obj1: ref count = 1, obj2: rec count = 1 **problem**
In my implementation, my obj2 ref count is 2, because of this code:
protected:
void retain() {
++(*_rc);
std::cout << "retained, rc: " << *_rc << std::endl;
}
void release() {
--(*_rc);
std::cout << "released, rc: " << *_rc << std::endl;
if (*_rc == 0) {
std::cout << "rc = 0, deleting obj" << std::endl;
delete _ptr;
_ptr = 0;
delete _rc;
_rc = 0;
}
}
private:
T *_ptr;
int *_rc;
// Delegate private copy constructor
template <typename U>
Sptr(const Sptr<U> *p) : _ptr(p->get()), _rc(p->rc()) {
if (p->get() != 0) retain();
}
// Delegate private assignment operator
template <typename U>
Sptr<T> &operator=(const Sptr<U> *p) {
if (_ptr != 0) release();
_ptr = p->get();
_rc = p->rc();
if (_ptr != 0) retain();
return *this;
}
public:
Sptr() : _ptr(0) {}
template <typename U>
Sptr(U *p) : _ptr(p) { _rc = new int(1); }
// Normal and template copy constructors both delegate to private
Sptr(const Sptr &o) : Sptr(&o) {
std::cout << "non-templated copy ctor" << std::endl;
}
template <typename U>
Sptr(const Sptr<U> &o) : Sptr(&o) {
std::cout << "templated copy ctor" << std::endl;
}
// Normal and template assignment operator
Sptr &operator=(const Sptr &o) {
std::cout << "non-templated assignment operator" << std::endl;
return operator=(&o);
}
template <typename U>
Sptr<T> &operator=(const Sptr<U> &o) {
std::cout << "templated assignment operator" << std::endl;
return operator=(&o);
}
// Assignment operator for assigning to void or 0
void operator=(int) {
if (_ptr != 0) release();
_ptr = 0;
_rc = 0;
}
The constructor is initialized with a ref count = 1, but in my assignment operator, I am retaining the object, making the ref count = 2. But it should only be 1 in this case, because bp2 = new B is only one pointer to that object. I've looked over various smart pointer implementation examples, and I can't seem to figure out how they deal with this case that I'm having trouble with.
Thanks for your time!
The assignment operator is riddled with small errors and unnecessary complex anyway. For example, when you assign Sptr<T> to itself it will have funny effects. Most manually written assignment operators should look like this:
T& T::operator= (T const& other) {
T(other).swap(*this);
return *this;
}
... or
T& T::operator= (T other) {
other.swap(*this);
return *this;
}
Once stateful allocators enter the game things change a bit but we can ignore this detail here. The main idea is to leverage the existing work done for the copy constructor and the destructor. Note, that this approach also works if the right hand side isn't T, i.e., you can still leverage a corresponding constructor, the destructor, and swap(). The only potentially additional work is desirable anyway, and trivial to implement: the swap() member. In your case that is very simple, too:
template <typename T>
void Sptr<T>::swap(Sptr<T>& other) {
std::swap(this->_ptr, other._ptr);
std::swap(this->_rc, other._rc);
}
Just a note on your assignment operator taking an int: This is a very bad idea! To reset the pointer, you should probably better have a reset() method. In C++ 2011 you could reasonably have a method taking a std::nullptr_t for this purpose, though.
Define an assignment operator for the raw pointer type. Otherwise, it will construct a new smart pointer with ref count 1, which you will then increase to 2.
Example:
template <typename U>
Sptr<T> &operator=(U *p)
{
if (_ptr != 0)
{
_ptr = p;
_rc = new int(1);
}
return *this;
}
This stuff is notoriously tricky, and there are probably more bugs waiting. I would make the constructor explicit to prevent other unintentional constructions.
From what I see of your code, you need a proper destructor in your smart pointer class to call release and fix the counter. You need at least that modification for your counter to work correctly.
I didn't see a Sptr(T *) constructor in your code, did you omit it?
As a side note, I would probably use a std::pair to store the counter and the T pointer, but your data layout works as it is. As another answer pointed it out, you should pay attention to the self assignment case though.
Here's a minimal implementation following your data layout and interface choices:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class Sptr {
protected:
T *_ptr;
int *_rc;
virtual void retain() {
if (_rc) // pointing to something
++(*_rc);
clog << "retain : " << *_rc << endl;
}
virtual void release() {
if (_rc) {
--(*_rc);
clog << "release : " << *_rc << endl;
if (*_rc == 0) {
delete _ptr;
_ptr = NULL;
delete _rc;
_rc = NULL;
}
}
}
public:
Sptr() : _ptr(NULL), _rc(NULL) {} // no reference
virtual ~Sptr() { release(); } // drop the reference held by this
Sptr(T *p): _ptr(p) { // new independent pointer
_rc = new int(0);
retain();
}
virtual Sptr<T> &operator=(T *p) {
release();
_ptr = p;
_rc = new int(0);
retain();
return *this;
}
Sptr(Sptr<T> &o) : _ptr(o._ptr), _rc(o._rc) {
retain();
}
virtual Sptr<T> &operator=(Sptr<T> &o) {
if (_rc != o._rc){ // different shared pointer
release();
_ptr = o._ptr;
_rc = o._rc;
retain();
}
return *this;
}
};
int main(){
int *i = new int(5);
Sptr<int> sptr1(i);
Sptr<int> sptr2(i);
Sptr<int> sptr3;
sptr1 = sptr1;
sptr2 = sptr1;
sptr3 = sptr1;
}
_rc is the indicator in your class that a pointer is shared with another instance or not.
For instance, in this code:
B *b = new B;
Sptr<B> sptr1(b);
Sptr<B> sptr2(b);
sptr1 and sptr2 are not sharing the pointer, because assignments were done separately, and thus the _rc should be different, which is effectively the case in my small implementation.