为受约束的 std::tuple 专门化 std::hash 是否被视为未定义行为?

sup*_*nun 9 c++ c++20

我知道专门std::hash针对将军std::tuple是未定义的行为。

\n

但是,如果专门std::hash针对特定的元组呢?

\n

例如,

\n
namespace std {\n  template<>\n  struct hash<std::tuple<MyType, float>> {\n    size_t operator()(const std::tuple<MyType, float>& t) const {\n        // ...\n    } \n  };\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

或者甚至std::tuple<Ts ...>使用某种required is_all_same_as_v<Ts\xe2\x80\xa6, MyType>C++20 约束来确保元组中的所有类型都完全相同MyType

\n

例如,这个

\n
namespace std {\n  template<typename... Ts>\n    requires (sizeof...(Ts) > 0 && is_all_same_as_v<Ts..., MyType>)\n  struct hash<std::tuple<Ts...>> {\n    size_t operator()(const std::tuple<Ts...>& t) const {\n        // ...\n    } \n  };\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

仍然被认为是未定义的行为吗?如果是这样,为什么?

\n

编辑:确保检查参数包的大小至少为 1,以避免专门化,std::hash<std::tuple<>>这绝对是未定义的行为。

\n

Bar*_*rry 8

[namespace.std]/2中的规则是:

除非明确禁止,否则程序可以将任何标准库类模板的模板专业化添加到命名空间,std前提是 (a) 添加的声明依赖于至少一种程序定义的类型,并且 (b) 专业化满足原始库的标准库要求模板。

其中来自[defns.prog.def.type]的程序定义类型是:

不属于 C++ 标准库且未由实现定义的非闭包类类型或枚举类型,或者非实现提供的 lambda 表达式的闭包类型,或者程序定义的专门化的实例化

您对std::hashfor的专门化std::tuple<MyType, float>很好,因为这取决于至少一种程序定义的类型 ( MyType) 并且满足 的要求std::hash

std::hash出于同样的原因,您对for std::tuple<Ts...>where all the Ts...are 的其他专业化MyType也很好(只要您的约束std::tuple<>专门消除)。这也是程序定义的类型。

使用什么机制来创建或限制专业化并不重要,只要您提供的任何专业化至少依赖于一种程序定义的类型即可。

(请注意,仅提供一些约束是不够的。我之前版本的答案将您的约束误读为简单地检查所有类型是否彼此相同。这是受约束的,但仍会为类似 的类型创建专门化std::tuple<std::string, std::string>,该类型没有其中程序定义的类型,因此将是未定义的行为)。

如果是这样,为什么?

这里的问题最终是一致性。谁可以在哪里专业化什么。如果标准库可以提供专业化,您不想与之发生冲突。更广泛地说,如果多个库尝试为常见事物定义专业化,那么如果每个库只为自己的类型提供专业化,那将特别有帮助 - 否则,如果多个库试图提供std::hash<std::tuple<int>>,那么这对任何人来说都不会很好。