8

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.

pythonic metaphor
  • 10,296
  • 18
  • 68
  • 110
  • Isn't `std::hash` a C++11 feature? In which case you can use `std::tuple` as well. I believe you are missing a `typename` keyword. – AJG85 Nov 18 '11 at 16:53
  • I think `boost::hash` is already defined for `boost::tuple`s. If you're using the standard library, use `std::tuple`, too! – Kerrek SB Nov 18 '11 at 16:58
  • @AJG85 I'm using gcc 4.3.4 where `hash` is still in `tr1` and `tuple` is only in boost. – pythonic metaphor Nov 18 '11 at 16:58
  • @Kerrek SB At least it's not in the version of boost I'm using, maybe in a more recent version. I was surprised by that too. – pythonic metaphor Nov 18 '11 at 16:59
  • @pythonic metaphor: I'm a VS2010 user these days, my previous comment was wrong it wasn't `typename` it was `template` that was missing. See Mankarse answer below. – AJG85 Nov 18 '11 at 17:00
  • @pythonicmetaphor: OK, maybe I should have said `std::tr1::tuple` from ``... – Kerrek SB Nov 18 '11 at 17:00

2 Answers2

9

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

Mankarse
  • 39,818
  • 11
  • 97
  • 141
6

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?

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • See http://stackoverflow.com/questions/2958648/what-are-the-pitfalls-of-adl and minor perhaps interface differences between std::tuple and boost::tuple. I know there are differences for std::basic_regex<> as well – sehe Nov 18 '11 at 16:57