Martin
Martin

Reputation: 9369

How can I specialize a template which depends on a static data member of a class?

Consider the following code. I want to specialize std::hash<> used in a Map, according to the value of a static data member of the Array class. Note that Array depends on Map itself.

// Array.h
#include <unordered_map>

using namespace std;

class Array;
typedef pair<int, int> Point;

namespace std {
    template <> struct hash<Point> {
        size_t operator()(const Point & p) const {
            return p.second * Array::C + p.first; // error: incomplete type ‘Array’ used in nested name specifier
        }
    };
}

typedef unordered_map<Point, Array, hash<Point> > Map;

class Array {
public:
    static const int R = 5, C = 5;
    Map compute() {/*...*/}
};

Of course, in the above specialization Array is not complete yet, so the compiler complains. However, if I move the specialization below the class definition, I obtain another error:

error: specialization of ‘std::hash<std::pair<int, int> >’ after instantiation

Upvotes: 3

Views: 291

Answers (2)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361442

I think you pretty much know the reason why you're getting that error, so I wouldn't explain that. However, if you want to explore that part also, then I will definitely explain that.

As for how to solve this problem, alright, I would do three things:

  • First, I wouldn't specialize std::hash to begin with!

  • Second, I would write my own MyHash class template, which I would instantiate with Array type, instead of Point type. Also note that the parameter type to operator() isn't Array, rather it is Point , as it should be.

  • Third, I would move the typedef of Map inside the class Array.

Here is what I mean:

template <typename A> 
struct MyHash
{
   size_t operator()(const Point & p) const 
   {
     return p.second * A::C + p.first;
   }
};

class Array {
public:
    typedef unordered_map<Point, Array, MyHash<Array> > Map;
    static const int R = 5, C = 5;
    Map compute() {/*...*/}
};

Online demo : http://ideone.com/hwLIT

Upvotes: 0

Kleist
Kleist

Reputation: 7985

You are using the static member before the class is defined. Fix this by moving the implementation of hash after Array:

// Array.h
#include <unordered_map>

using namespace std;

class Array;
typedef pair<int, int> Point;

namespace std {
    template <> struct hash<Point>;
}

typedef unordered_map<Point, Array, hash<Point> > Map;
class Array {
public:
    static const int R = 5, C = 5;
    Map compute();
};

namespace std {
    template <> struct hash<Point> {
        size_t operator()(const Point & p) const {
            return p.second * Array::C + p.first;
        }
    };
}

Upvotes: 1

Related Questions