Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using boost::tuple in tr1::hash

Tags:

c++

templates

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.

like image 597
pythonic metaphor Avatar asked Nov 18 '11 16:11

pythonic metaphor


1 Answers

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 ().

like image 168
Mankarse Avatar answered Sep 27 '22 20:09

Mankarse