为受约束的std::tuple特化std::hash是否属于未定义行为?
问题解答
针对std::tuple<MyType, float>的显式特化
像你写的这种全特化写法合法,不属于未定义行为:
namespace std { template<> struct hash<std::tuple<MyType, float>> { size_t operator()(const std::tuple<MyType, float>& t) const { // ... } }; }
按C++标准对std命名空间的规则:用户可以在std里给标准库模板添加显式全特化,只要特化的模板参数中至少包含一个用户自定义类型(这里MyType是你自定义的类型,满足要求),且特化的hash结构符合标准库对hash的基本要求——比如operator()需是无副作用的纯函数,返回的哈希值要满足哈希函数的一致性等。
带C++20约束的std::tuple<Ts...>部分特化
而这种带约束的部分特化写法属于未定义行为:
namespace std { template<typename... Ts> requires (sizeof...(Ts) > 0 && is_all_same_as_v<Ts..., MyType>) struct hash<std::tuple<Ts...>> { size_t operator()(const std::tuple<Ts...>& t) const { // ... } }; }
原因如下:
- C++标准明确禁止用户在
std命名空间中为标准库模板添加部分特化,无论是否带有requires约束。哪怕你限定了仅匹配全为MyType的tuple,这本质上还是对std::hash模板的部分特化,违反了std命名空间的扩展规则。 - 标准仅允许用户对标准库模板进行显式全特化,且需满足特化参数包含用户自定义类型的条件,部分特化不在允许范围内。
额外说明
你提到的“通用std::tuple特化属于UB”,指的就是这类针对任意std::tuple<Ts...>的部分特化,和第二个例子性质一致,因此均为未定义行为。而第一个例子的全特化因符合标准允许的规则,所以是合法的。
内容的提问来源于stack exchange,提问作者supernun
相关产品推荐
相关产品推荐

