pythonic metaphor
pythonic metaphor

Reputation: 10546

Using boost::tuple in tr1::hash

I want to define std::tr1::hash<boost::tuple<A,B,C> >. But I get an error that doesn't appear when I give a complete instantation. Here's the code

namespace std{

namespace tr1{
template<typename A, typename B, typename C>
struct hash<boost::tuple<A,B,C> >{
    size_t operator()(const boost::tuple<A,B,C> &t) const{
        size_t seed = 0;
        boost::hash_combine(seed, t.get<0>());
        boost::hash_combine(seed, t.get<1>());
        boost::hash_combine(seed, t.get<2>());
        return seed;
    }
};

template<>
struct hash<boost::tuple<int,int,int> >{
    size_t operator()(const boost::tuple<int,int,int> &t) const{
        size_t seed = 0;
        boost::hash_combine(seed, t.get<0>());
        boost::hash_combine(seed, t.get<1>());
        boost::hash_combine(seed, t.get<2>());
        return seed;
    }
};
}
}

The first piece gives this error

unordered.hpp: In member function 'size_t std::tr1::hash<boost::tuples::tuple<A, B, C, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type> >::operator()(const boost::tuples::tuple<A, B, C, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>&) const':
unordered.hpp:12: error: expected primary-expression before ')' token
unordered.hpp:13: error: expected primary-expression before ')' token
unordered.hpp:14: error: expected primary-expression before ')' token

and the second compiles just fine. What's wrong with the first template? I'm using gcc 4.3.4.

Upvotes: 8

Views: 895

Answers (2)

Mankarse
Mankarse

Reputation: 40603

You need to use the .template keyword:

template<typename A, typename B, typename C>
struct hash<boost::tuple<A,B,C> >{
    size_t operator()(const boost::tuple<A,B,C> &t) const{
        size_t seed = 0;
        boost::hash_combine(seed, t.template get<0>());
        boost::hash_combine(seed, t.template get<1>());
        boost::hash_combine(seed, t.template get<2>());
        return seed;
    }
};

This is required because type of t depends on three template paramaters (and so t is type-dependent), and get<0> is the name of a template specialization. From the C++ standard -- §14.2/4:

When the name of a member template specialization appears after . or -> in a postfix-expression ... and the object expression of the postfix-expression is type-dependent ... the member template name must be prefixed by the keyword template. ...

This requirement exists to allow templates to be parsed before their type arguments are known.

For example, consider:

f . set < 0 > ( 2 == 3 )

Without the .template rule, this could interpreted as two different things:

//A call to an instantiation of a member function template
//in this case equivalent to f.template set<0>(false)
f.set<0>(2 == 3)
//A series of comparison operations, in this case equivalent to
//f.set < 0
f.set < 0 > (2 == 3)

The actual rules allow f . set < 0 > ( 2 == 3 ) to be unambiguously parsed as a series of comparison operations. They also mean that t.get<0>() is parsed as t.get < 0 > (). The expected primary-expression is meant to be in the empty ().

Upvotes: 9

sehe
sehe

Reputation: 392929

I don't have time to check things but I'd expect either

std::get<0>(t)

or

boost::get<0>(t)

instead of t.get<0>()

Do qualify get() even if you are 'using' namespaces, or ADL will hurt you badly when mixing libraries like this. See What are the pitfalls of ADL?

Upvotes: 6

Related Questions