1

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;
    }

11
  • The error is telling you that you have no constructor to convert from std::stared_ptr to my_shared_ptr, and you are trying to do that in Blob constructor. I guess you wanted to use make_shared instead of std::make_shared there. Commented Jul 29, 2020 at 13:00
  • 1
    make_shared is broken. It should not take T, but Args...: template<class T, class... Args> my_shared_ptr<T> make_shared(Args&&... args) { return my_shared_ptr<T>(new T(std::forward<Args>(args)...)); }. Commented Jul 29, 2020 at 13:01
  • @Evg its not broken, it isn't the same as std::make_shared nor as efficient but should work for copyable types. Commented Jul 29, 2020 at 13:15
  • Please show a minimal reproducible example including where you are using my_shared_ptr as it could be the source of the error Commented Jul 29, 2020 at 13:16
  • Are you trying to use my_shared_ptr and std::shared_ptr at the same time? data(std::make_shared<std::vector<T>>(il)) I don't think this is what you wanted to do. Commented Jul 29, 2020 at 13:20

1 Answer 1

1

The compiler error says that it calls std::make_shared, which returns std::shared_ptr, which you try to pass into my_shared_ptr. my_shared_ptr doesn't have a constructor taking std::shared_ptr.

One easy fix is to call make_shared and not std::make_shared. A better fix is to rename your make_shared into my_make_shared, so that ADL never selects std::make_shared over your make_shared.

Sign up to request clarification or add additional context in comments.

6 Comments

Many thanks for the reply. I added an update to the original post to address the issue of mixing std::make_shared and my_shared_ptr. Unfortunately, using my own implementation of make_shared still results in errors.
@MasterReDWinD If you look into the error message, which you should, it tells you that it passes std::shared_ptr into my_shared_ptr because it finds std::make_shared through ADL. Rename yours to my_make_shared.
I've made the change you suggested. For some reason the std::shared_ptr constructor is still being called: 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(my_make_shared<std::vector<T>>(il)) { }
What does my_make_shared return?
The return type is my_shared_ptr<T>.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.