c++template-specializationtemplate-instantiation

How to resolve specialisation after instantiation issue?


In my code I'd like to make unordered sets with only one template parameter (if possible) like so:

std::unordered_set<std::shared_ptr<Foo>> my_foos;

The problem is that my Foo class also have a class member which is of the type std::unordered_set<std::shared_ptr<Foo>>. Specifically my Foo class looks something like this:

#include<unordered_set>
#include<memory>

class Foo {
  explicit Foo(int val) : val_(val) {}
  void AddFoo(Foo& foo){foos.insert(std::make_shared<Foo>(foo));}
  std::unordered_set<std::shared_ptr<Foo>> foos;
  int val_;
};

namespace std {
template <>  
struct hash<shared_ptr<Foo>> {
  size_t operator()(const Foo shared_ptr<Foo>& foo) const {
    return hash<int>()(foo->val_);
  }
};

template <>
struct equal_to<Foo> {
  bool operator()(const Foo shared_ptr<Foo>& lhs, const Foo shared_ptr<Foo>& rhs) const {
    return lhs->val_ == rhs->val_;
  }
};
}

This gives the error

error: specialization of 'std::hash<std::shared_ptr<Foo> >' after instantiation

How can I resolve this issue?


Solution

  • I got your code to compile. Had to add forward declarations, separate definition from declaration (otherwise it couldn't find Foo's members) for those structs, added public access specifier in Foo. Also, there was some code to cleanup in the function signatures.

    #include <memory>
    #include <unordered_set>
    
    class Foo;  // forward declaration of Foo here
    
    namespace std {
    template <>
    struct hash<shared_ptr<Foo>> {
        size_t operator()(const shared_ptr<Foo>& foo) const;
    };
    
    template <>
    struct equal_to<Foo> {
        bool operator()(const shared_ptr<Foo>& lhs,
                        const shared_ptr<Foo>& rhs) const;
    };
    }  // namespace std
    
    class Foo {
    public:
        explicit Foo(int val) : val_(val) {}
        void AddFoo(Foo& foo) { foos.insert(std::make_shared<Foo>(foo)); }
        std::unordered_set<std::shared_ptr<Foo>> foos;
        int val_;
    };
    
    size_t std::hash<std::shared_ptr<Foo>>::operator()(
        const std::shared_ptr<Foo>& foo) const {
        return std::hash<int>()(foo->val_);
    }
    
    bool std::equal_to<Foo>::operator()(const std::shared_ptr<Foo>& lhs,
                                        const std::shared_ptr<Foo>& rhs) const {
        return lhs->val_ == rhs->val_;
    }