[英]Partial specialization and the need for std::void_t<>
语言律师之一......
我在玩 SFINAE 和 TMP,试图获得更深入的理解。
考虑下面的代码,一个 std::is_default_constructible 的简单实现
#include <type_traits>
template <typename T, typename = void> struct is_default_constructable : std::false_type {};
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
class NC { NC(int); }; // Not default constructable
#include <iostream>
int main(int, char **)
{
std::cout << "int is_default_constructible? " << is_default_constructable<int>() << std::endl;
std::cout << "NC is_default_constructible? " << is_default_constructable<NC>() << std::endl;
}
这编译得很好,但实际上不起作用,它对所有类型都返回 false。
对于 NC 情况,正如我所期望的那样, T()
格式不正确,因此由于 SFINAE 丢弃了专业化并使用了主要模板 (false_type)。 但是对于int
情况,我希望将专业化用作decltype(T())
是有效的并且等效于T
。
如果根据<type_traits>
的实际代码,我将专业化更改为
template <typename T> using wrap = void;
template <typename T> struct is_default_constructable<T, wrap<decltype(T())> > : std::true_type {};
( std::void_t<>
第二个模板参数包装在std::void_t<>
的模型中,这迫使第二个类型为void
),这按预期工作。
更奇怪的是,使用void
以外的类型作为主模板或wrap<>
的默认类型的这种方案的变体也会失败,除非这两种类型相同。
有人可以解释为什么wrap<>
的类型和第二个模板参数默认类型需要相同才能选择专业化吗?
(我在 g++ 6.3 版中使用“g++ -Wall --std=c++17”,但我认为这与编译器无关。)
这不是 SFINAE 或部分专业化排序的结果,而是由于使用了默认模板参数。 非正式地,原因是默认模板参数的应用发生在搜索模板定义之前,包括可能的特化。
所以在上面的例子中,说is_default_constructable<int>
的代码实际上是在应用默认的第二个参数后请求实例化模板is_default_constructable<int, void>
。 然后考虑可能的定义。
“主要”模板定义明确匹配并包含在内。 给定的部分专业化
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
实际上是定义is_default_constructable<int, int>
,它与请求的is_default_constructable<int, void>
不匹配is_default_constructable<int, void>
因此即使替换成功,也会忽略特化。 这将主要定义(继承 false_type)作为唯一可行的定义,因此它被选中。
当专业化具有wrap<>
(或std::void_t<>
)以强制第二个 arg 为void
,专业化定义is_default_constructable<int, void>
与请求匹配。 这个定义(假设替换成功,即T()
格式良好)比主要定义更专业(根据超复杂的专业化排序规则),所以选择它。
顺便说一句,当 T 是引用类型或其他极端情况时,上述幼稚的实现可能无法按预期工作,这是使用所有这些标准库版本的一个很好的理由。 他们标准委员会的人比我聪明得多,并且已经想到了所有这些事情。
而且,是的,我无法拼写可构造的,假设它甚至是一个单词。 这是使用标准库的另一个很好的理由。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.