I have written an implementation of Shared_ptr as part of working through the C++ Primer book. The header file does compile correctly, however I'm receiving several "No matching function" errors when I try to use it with a `Blob' class.
I've spent a few hours staring at the code and I think the problem is with the constructors, however I'm still a C++ beginner and haven't had any luck fixing it. I previously posted this on code review but was told it was 'off topic' and was instructed to post it here. The other posts on shared_ptr implementations there didn't help. Any feedback to set me in the right direction would be greatly appreciated.
Note: I haven't posted the Blob class as it is quite long, nor have I posted the test class. I'm happy to provide either if requested.
Example errors:
exercise_16.29.h: In instantiation of 'Blob< <template-parameter-1-1> >::Blob(std::initializer_list<_Tp>) [with T = int]':
exercise_16.30.cpp:9:33: required from here
exercise_16.29.h:58:103: error: no matching function for call to 'my_shared_ptr<std::vector<int, std::allocator<int> > >::my_shared_ptr(std::shared_ptr<std::vector<int, std::allocator<int> > >)'
template <typename T> Blob<T>::Blob(std::initializer_list<T> il): data(std::make_shared<std::vector<T>>(il)) { }
^
In file included from exercise_16.29.h:10:
exercise_16.28-1.h:50:1: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(const my_shared_ptr< <template-parameter-1-1> >&, void (*)(T*)) [with T = std::vector<int, std::allocator<int> >]'
my_shared_ptr<T>::my_shared_ptr(const my_shared_ptr &rhs, void (*d)(T*)): p(rhs.p), del(d), count(rhs.count) {
^~~~~~~~~~~~~~~~
exercise_16.28-1.h:50:1: note: candidate expects 2 arguments, 1 provided
exercise_16.28-1.h:44:1: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(const my_shared_ptr< <template-parameter-1-1> >&) [with T = std::vector<int, std::allocator<int> >]'
my_shared_ptr<T>::my_shared_ptr(const my_shared_ptr &rhs): p(rhs.p), del(nullptr), count(rhs.count) {
^~~~~~~~~~~~~~~~
exercise_16.28-1.h:44:1: note: no known conversion for argument 1 from 'std::shared_ptr<std::vector<int, std::allocator<int> > >' to 'const my_shared_ptr<std::vector<int, std::allocator<int> > >&'
exercise_16.28-1.h:20:12: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(T*, void (*)(T*)) [with T = std::vector<int, std::allocator<int> >]'
explicit my_shared_ptr(T *pt, void (*d)(T*)): p(pt), count(new std::size_t(1)), del(d) { } //shared_ptr<T> p(q, d)
^~~~~~~~~~~~~
exercise_16.28-1.h:20:12: note: candidate expects 2 arguments, 1 provided
exercise_16.28-1.h:19:12: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(T*) [with T = std::vector<int, std::allocator<int> >]'
explicit my_shared_ptr(T *pt): p(pt), count(new std::size_t(1)), del(nullptr) { } // shared_ptr<T> p(q)
^~~~~~~~~~~~~
exercise_16.28-1.h:19:12: note: no known conversion for argument 1 from 'std::shared_ptr<std::vector<int, std::allocator<int> > >' to 'std::vector<int, std::allocator<int> >*'
exercise_16.28-1.h:16:3: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr() [with T = std::vector<int, std::allocator<int> >]'
my_shared_ptr(): p(nullptr), del(nullptr), count(nullptr) { }
^~~~~~~~~~~~~
exercise_16.28-1.h:16:3: note: candidate expects 0 arguments, 1 provided
exercise_16.28-1.h: In instantiation of 'my_shared_ptr< <template-parameter-1-1> >::~my_shared_ptr() [with T = std::vector<int, std::allocator<int> >]':
Shared_ptr implementation:
#ifndef EXERCISE_16_28_1_H
#define EXERCISE_16_28_1_H
#include <cstddef>
#include <algorithm>
template <typename> class my_shared_ptr;
template <typename T> void swap(my_shared_ptr<T>&, my_shared_ptr<T>&);
template <typename T> my_shared_ptr<T> make_shared(T);
template <typename T> class my_shared_ptr {
friend void swap<T>(my_shared_ptr<T>&, my_shared_ptr<T>&);
friend my_shared_ptr make_shared<T>(T);
public:
my_shared_ptr(): p(nullptr), del(nullptr), count(nullptr) { }
//my_shared_ptr(T *pt): p(pt), count(new std::size_t(1)), del(nullptr) { }
//my_shared_ptr(T *pt, void (*d)(T*)): p(pt), count(new std::size_t(1)), del(d) { }
explicit my_shared_ptr(T *pt): p(pt), count(new std::size_t(1)), del(nullptr) { } // shared_ptr<T> p(q)
explicit my_shared_ptr(T *pt, void (*d)(T*)): p(pt), count(new std::size_t(1)), del(d) { } //shared_ptr<T> p(q, d)
my_shared_ptr(const my_shared_ptr&); // copy constructor
my_shared_ptr(const my_shared_ptr&, void (*d)(T*)); // copy constructor with deleter
my_shared_ptr& operator=(my_shared_ptr&); // copy assignment operator
~my_shared_ptr(); // destructor
T& operator*() const;
T* operator->() const;
T* get();
my_shared_ptr& swap(my_shared_ptr&);
bool unique() const;
std::size_t use_count() const;
my_shared_ptr& reset();
my_shared_ptr& reset(T*);
my_shared_ptr& reset(T*, void (*d)(T*));
private:
T* p;
void (*del)(T*);
std::size_t *count;
};
// copy constructor
template <typename T>
my_shared_ptr<T>::my_shared_ptr(const my_shared_ptr &rhs): p(rhs.p), del(nullptr), count(rhs.count) {
++*count;
}
// copy constructor with deleter
template <typename T>
my_shared_ptr<T>::my_shared_ptr(const my_shared_ptr &rhs, void (*d)(T*)): p(rhs.p), del(d), count(rhs.count) {
++*count;
}
// copy assignment operator
template <typename T>
my_shared_ptr<T>& my_shared_ptr<T>::operator=(my_shared_ptr &rhs) {
++*rhs.count;
--*count;
if(--*count == 0) {
del ? del(p) : delete p;
del ? del(count) : delete count;
}
p = rhs.p;
del = rhs.del;
return *this;
}
// destructor
template <typename T>
my_shared_ptr<T>::~my_shared_ptr() {
if(--*count == 0) {
del ? del(p) : delete p;
del ? del(count) : delete count;
}
del = nullptr;
}
// dereference operator
template <typename T>
T& my_shared_ptr<T>::operator*() const {
return *p;
}
// member access operator
template <typename T>
T* my_shared_ptr<T>::operator->() const {
return & this->operator*();
}
// get
template <typename T>
T* my_shared_ptr<T>::get() {
return p;
}
// member swap
template <typename T>
my_shared_ptr<T>& my_shared_ptr<T>::swap(my_shared_ptr &rhs) {
using std::swap;
swap(p, rhs.p);
swap(del, rhs.del);
return *this;
}
// unique
template <typename T>
bool my_shared_ptr<T>::unique() const {
if (*count == 1)
return true;
else
return false;
}
// use_count
template <typename T>
std::size_t my_shared_ptr<T>::use_count() const {
return count;
}
template <typename T> my_shared_ptr<T>& my_shared_ptr<T>::reset() {
if (--*count == 0) {
delete p;
delete count;
}
del = nullptr;
return *this;
}
template <typename T> my_shared_ptr<T>& my_shared_ptr<T>::reset(T *t) {
if (--*count == 0) {
delete p;
delete count;
}
p = t.p;
del = nullptr;
return *this;
}
template <typename T> my_shared_ptr<T>& my_shared_ptr<T>::reset(T *t, void (*d)(T*)) {
if (--*count == 0) {
delete p;
delete count;
}
p = t.p;
del = d;
return *this;
}
// make_shared
template <typename T>
my_shared_ptr<T> make_shared(T t) {
return my_shared_ptr<T>(new T(t));
}
// non-member swap
template <typename T> inline void swap(my_shared_ptr<T> &lhs, my_shared_ptr<T> &rhs) {
using std::swap;
swap(lhs.p, rhs.p);
swap(lhs.del, rhs.del);
}
#endif
Update:
Many thanks for the helpful replies so far. Using std::make_shared rather than make_shared was a debugging change I made, however I overlooked the fact that std::make_shared would return std::shared_ptr not my_shared_ptr. I have added the original compiler error message below where Blob uses my implementation of make_shared:
exercise_16.30.cpp:9:33: required from here
exercise_16.29.h:58:103: error: no matching function for call to 'my_shared_ptr<std::vector<int, std::allocator<int> > >::my_shared_ptr(std::shared_ptr<std::vector<int, std::allocator<int> > >)'
template <typename T> Blob<T>::Blob(std::initializer_list<T> il): data(make_shared<std::vector<T>>(il)) { }
^
In file included from exercise_16.29.h:10:
exercise_16.28-1.h:50:1: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(const my_shared_ptr< <template-parameter-1-1> >&, void (*)(T*)) [with T = std::vector<int, std::allocator<int> >]'
my_shared_ptr<T>::my_shared_ptr(const my_shared_ptr &rhs, void (*d)(T*)): p(rhs.p), del(d), count(rhs.count) {
^~~~~~~~~~~~~~~~
exercise_16.28-1.h:50:1: note: candidate expects 2 arguments, 1 provided
exercise_16.28-1.h:44:1: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(const my_shared_ptr< <template-parameter-1-1> >&) [with T = std::vector<int, std::allocator<int> >]'
my_shared_ptr<T>::my_shared_ptr(const my_shared_ptr &rhs): p(rhs.p), del(nullptr), count(rhs.count) {
^~~~~~~~~~~~~~~~
exercise_16.28-1.h:44:1: note: no known conversion for argument 1 from 'std::shared_ptr<std::vector<int, std::allocator<int> > >' to 'const my_shared_ptr<std::vector<int, std::allocator<int> > >&'
exercise_16.28-1.h:20:12: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(T*, void (*)(T*)) [with T = std::vector<int, std::allocator<int> >]'
explicit my_shared_ptr(T *pt, void (*d)(T*)): p(pt), count(new std::size_t(1)), del(d) { } //shared_ptr<T> p(q, d)
^~~~~~~~~~~~~
exercise_16.28-1.h:20:12: note: candidate expects 2 arguments, 1 provided
exercise_16.28-1.h:19:12: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr(T*) [with T = std::vector<int, std::allocator<int> >]'
explicit my_shared_ptr(T *pt): p(pt), count(new std::size_t(1)), del(nullptr) { } // shared_ptr<T> p(q)
^~~~~~~~~~~~~
exercise_16.28-1.h:19:12: note: no known conversion for argument 1 from 'std::shared_ptr<std::vector<int, std::allocator<int> > >' to 'std::vector<int, std::allocator<int> >*'
exercise_16.28-1.h:16:3: note: candidate: 'my_shared_ptr< <template-parameter-1-1> >::my_shared_ptr() [with T = std::vector<int, std::allocator<int> >]'
my_shared_ptr(): p(nullptr), del(nullptr), count(nullptr) { }
^~~~~~~~~~~~~
exercise_16.28-1.h:16:3: note: candidate expects 0 arguments, 1 provided
exercise_16.28-1.h: In instantiation of 'my_shared_ptr< <template-parameter-1-1> >::~my_shared_ptr() [with T = std::vector<int, std::allocator<int> >]':```
As requested I have now included the Blob class and test code:
#ifndef BLOB_H
#define BLOB_H
#include <initializer_list>
#include <vector>
#include <string>
#include <memory>
#include <stdexcept>
#include <iostream>
#include "exercise_16.28-1.h"
template <typename> class BlobPtr;
template <typename> class Blob; // needed for parameters in operators below
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&);
template <typename T> bool operator!=(const Blob<T>&, const Blob<T>&);
template <typename T> bool operator<(const Blob<T>&, const Blob<T>&);
template <typename T> bool operator>(const Blob<T>&, const Blob<T>&);
template <typename T> bool operator<=(const Blob<T>&, const Blob<T>&);
template <typename T> bool operator>=(const Blob<T>&, const Blob<T>&);
template <typename T> class Blob {
friend class BlobPtr<T>;
friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
friend bool operator!=<T>(const Blob<T>&, const Blob<T>&);
friend bool operator< <T>(const Blob<T>&, const Blob<T>&);
friend bool operator><T>(const Blob<T>&, const Blob<T>&);
friend bool operator<=<T>(const Blob<T>&, const Blob<T>&);
friend bool operator>=<T>(const Blob<T>&, const Blob<T>&);
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
// constructors
Blob();
Blob(std::initializer_list<T> il);
template <typename It> Blob(It b, It e);
// members
size_type size() const { return data->size(); };
bool empty() const { return data->empty(); }
// add and remove elements
void push_back(const T &t) { data->push_back(t); }
void push_back(T &&t) { data->push_back(std::move(t)); }
void pop_back();
// element access
T& front();
T& back();
const T& front() const;
const T& back() const;
T& operator[](size_type);
const T& operator[](size_type) const;
BlobPtr<T> begin() const; // return BlobPtr to the first element
BlobPtr<T> end() const ; // and one past the last element
private:
my_shared_ptr<std::vector<T>> data;
void check(size_type i, const std::string &msg) const;
};
template <typename T> Blob<T>::Blob(): data(make_shared<std::vector<T>>()) { }
template <typename T> Blob<T>::Blob(std::initializer_list<T> il): data(make_shared<std::vector<T>>(il)) { }
template <typename T>
template <typename It> Blob<T>::Blob(It b, It e): data(make_shared<std::vector<T>>(b, e)) {}
template <typename T> void Blob<T>::check(size_type i, const std::string &msg) const {
if (i >= data->size())
throw std::out_of_range(msg);
}
template <typename T> T& Blob<T>::front() {
// if the vector is empty, check will throw
check(0, "front on empty Blob");
return data->front();
}
template <typename T> const T& Blob<T>::front() const {
// if the vector is empty, check will throw
check(0, "front on empty Blob");
return data->front();
}
template <typename T> T& Blob<T>::back() {
check(0, "back on empty Blob");
return data->back();
}
template <typename T> const T& Blob<T>::back() const {
check(0, "back on empty Blob");
return data->back();
}
template <typename T> T& Blob<T>::operator[](size_type i) {
return data->at(i);
}
template <typename T> const T& Blob<T>::operator[](size_type i) const {
return data->at(i);
}
template <typename T> void Blob<T>::pop_back() {
check(0, "pop_back on empty Blob");
data->pop_back();
}
template <typename T> bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {
return *lhs.data == *rhs.data;
}
template <typename T> bool operator!=(const Blob<T> &lhs, const Blob<T> &rhs) {
return !(lhs == rhs);
}
template <typename T> bool operator<(const Blob<T> &lhs, const Blob<T> &rhs) {
return *lhs.data < *rhs.data;
}
template <typename T> bool operator>(const Blob<T> &lhs, const Blob<T> &rhs) {
return rhs < lhs;
}
template <typename T> bool operator<=(const Blob<T> &lhs, const Blob<T> &rhs) {
return !(lhs > rhs);
}
template <typename T> bool operator>=(const Blob<T> &lhs, const Blob<T> &rhs) {
return !(lhs < rhs);
}
template <typename T> bool operator==(const BlobPtr<T>&, const BlobPtr<T>&);
template <typename T> bool operator!=(const BlobPtr<T>&, const BlobPtr<T>&);
template <typename T> bool operator<(const BlobPtr<T>&, const BlobPtr<T>&);
template <typename T> bool operator>(const BlobPtr<T>&, const BlobPtr<T>&);
template <typename T> bool operator<=(const BlobPtr<T>&, const BlobPtr<T>&);
template <typename T> bool operator>=(const BlobPtr<T>&, const BlobPtr<T>&);
// BlobPtr throws an exception on attempts to access a nonexistent element
template <typename T> class BlobPtr {
friend bool operator==<T>(const BlobPtr<T>&, const BlobPtr<T>&);
friend bool operator!=<T>(const BlobPtr<T>&, const BlobPtr<T>&);
friend bool operator< <T>(const BlobPtr<T>&, const BlobPtr<T>&);
friend bool operator><T>(const BlobPtr<T>&, const BlobPtr<T>&);
friend bool operator<=<T>(const BlobPtr<T>&, const BlobPtr<T>&);
friend bool operator>=<T>(const BlobPtr<T>&, const BlobPtr<T>&);
public:
BlobPtr(): curr(0) { }
BlobPtr(const Blob<T> &a, size_t sz = 0): wptr(a.data), curr(sz) { }
T& operator*() const {
auto p = check(curr, "derefernce past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
BlobPtr& operator++(); // prefix operators
BlobPtr& operator--();
T& deref() const;
BlobPtr<T>& incr(); // prefix version
private:
// check returns a shared_ptr to the vector if the check succeeds
std::shared_ptr<std::vector<T>>
check(std::size_t, const std::string&) const;
// store a weak_ptr, which means the underlying vector might be destroyed
std::weak_ptr<std::vector<T>> wptr;
std::size_t curr; // current position within the vector
};
template <typename T> std::shared_ptr<std::vector<T>>
BlobPtr<T>::check(std::size_t i, const std::string &msg) const {
auto ret = wptr.lock(); // is the vector still around?
if (!ret)
throw std::runtime_error("unbound BlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg);
return ret; // otherwise, return a shared_ptr to the vector
}
template <typename T> BlobPtr<T>& BlobPtr<T>::operator++() {
// if curr already points past the end of the container, can't increment it
check(curr, "increment exceeds bounds");
++curr;
return *this;
}
template <typename T> BlobPtr<T>& BlobPtr<T>::operator--() {
// if curr is zero, decrementing it will yield an invalid subscript
--curr; // move the current state back one element
check(curr, "decrement exceeds bounds");
return *this;
}
template <typename T> T& BlobPtr<T>::deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
// prefix: return a reference to the incremented object
template <typename T> BlobPtr<T>& BlobPtr<T>::incr() {
// if curr already points past the end of the container, can't increment it
check(curr, "increment past end of BlobPtr");
++curr; // advance the current state
return *this;
}
template <typename T> bool operator==(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) {
return lhs.deref() == rhs.deref();
}
template <typename T> bool operator!=(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) {
return !(lhs == rhs);
}
template <typename T> bool operator<(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) {
return lhs.deref() < rhs.deref();
}
template <typename T> bool operator>(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) {
return rhs < lhs;
}
template <typename T> bool operator<=(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) {
return !(lhs > rhs);
}
template <typename T> bool operator>=(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) {
return !(lhs < rhs);
}
template <typename T> BlobPtr<T> Blob<T>::begin() const {
return BlobPtr<T>(*this);
}
template <typename T> BlobPtr<T> Blob<T>::end() const {
return BlobPtr<T>(*this, data->size());
}
#endif
#include "exercise_16.29.h"
#include <iostream>
#include <string>
#include <vector>
#include <list>
int main()
{
Blob<int> ib{21, 53, 84, 91, 23};
for (const auto &elem : ib)
std::cout << elem << ' ';
std::cout << '\n';
Blob<std::string> sb{"21", "53", "84", "91", "23"};
for (const auto &elem : sb)
std::cout << elem << ' ';
std::cout << '\n';
std::cout << '\n';
Blob<std::vector<std::string>> vb{ {"21", "5"}, {"53", "23", "42", "23"},
{"84", "59"}, {"91", "68"}, {"23", "72", "10" } };
for (const auto &vector : vb) {
for (const auto &elem : vector)
std::cout << elem << ' ';
std::cout << '\n';
}
int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<long> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::list<const char*> w = {"now", "is", "the", "time"};
Blob<int> a1(std::begin(ia), std::end(ia));
Blob<int> s2(vi.cbegin(), vi.cend());
Blob<std::string> a3(w.cbegin(), w.cend());
return 0;
}