在tr1::hash中使用boost::tuple

8

我想定义std::tr1::hash<boost::tuple<A,B,C> >,但是当我给出完整的实例化时不会出现错误。以下是代码:

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;
    }
};
}
}

第一段出现了错误

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

第一个模板有问题,无法编译通过。第二个模板没有问题。我使用的是gcc 4.3.4。


std::hash 不是 C++11 的特性吗?如果是的话,您也可以使用 std::tuple。我认为您缺少了一个 typename 关键字。 - AJG85
我认为boost::hash已经为boost::tuple定义过了。如果您正在使用标准库,请也使用std::tuple - Kerrek SB
@AJG85 我正在使用gcc 4.3.4,在那里hash仍然在tr1中,而tuple只在boost中。 - pythonic metaphor
@Kerrek SB 至少在我使用的boost版本中没有,也许在更新的版本中有。我也很惊讶。 - pythonic metaphor
@pythonic隐喻:我现在是一个VS2010用户,我的先前评论是错误的,缺失的不是“typename”,而是“template”。请参见下面Mankarse的答案。 - AJG85
@pythonicmetaphor:好吧,也许我应该说 <tr1/tuple> 中的 std::tr1::tuple... - Kerrek SB
2个回答

9
您需要使用.template关键字:
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;
    }
};

这是必要的,因为t的类型取决于三个模板参数(因此t是类型相关的),而get<0>是模板特化的名称。根据C++标准-§14.2/4

当成员模板特化的名称出现在后缀表达式中的“.”或“->”之后时...并且后缀表达式的对象表达式是类型相关的...则成员模板名称必须由关键字template前缀。...

该要求存在是为了允许在未知其类型参数的情况下解析模板。
例如,请考虑:
f . set < 0 > ( 2 == 3 )

没有 .template 规则,这可能会被解释为两个不同的事物:
//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)

实际规则允许将f . set < 0 > ( 2 == 3 )解析为一系列比较操作,这也意味着t.get<0>()被解析为t.get < 0 > ()。预期的主表达式应该在空的()中。

6
我没有时间检查事情,但我期望其中之一。
std::get<0>(t)

或者

boost::get<0>(t)

使用命名空间时,即使您正在“使用”它们,也要对get()进行限定,否则在混合库时ADL会给您带来很大的伤害。请参阅ADL的陷阱是什么?,而不是使用t.get<0>()


请参阅https://dev59.com/9nA85IYBdhLWcg3wD_O8,以及std::tuple和boost::tuple之间可能存在的一些接口差异。我知道std::basic_regex<>也有一些区别。 - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接