【问题标题】:Partial specialization and the need for std::void_t<>部分专业化和对 std::void_t<> 的需求
【发布时间】:2018-10-18 00:11:31
【问题描述】:

给语言律师一个....

我正在玩 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

如果基于&lt;type_traits&gt;中的实际代码,我将专业化改为

template <typename T> using wrap = void;
template <typename T> struct is_default_constructable<T, wrap<decltype(T())> > : std::true_type {};

(即将第二个模板参数包装在 std::void_t&lt;&gt; 的模型中,强制第二个类型为 void),这可以按预期工作。

更奇怪的是,这种方案的变体使用void 以外的类型作为主模板中的默认类型或wrap&lt;&gt; 也失败了,除非这两种类型相同。 p>

有人可以解释为什么wrap&lt;&gt; 的类型和第二个模板参数默认类型需要相同才能选择专业化吗?

(我在 g++ 6.3 版中使用“g++ -Wall --std=c++17”,但我认为这与编译器无关。)

【问题讨论】:

    标签: c++ language-lawyer partial-specialization


    【解决方案1】:

    这不是 SFINAE 或部分专业化排序的结果,而是由于使用了默认模板参数。非正式地,原因是默认模板参数的应用发生在搜索模板定义(包括可能的特化)之前。

    所以在上述情况下,is_default_constructable&lt;int&gt; 的代码实际上是在应用默认的第二个参数后请求实例化模板is_default_constructable&lt;int, void&gt;。然后考虑可能的定义。

    “主要”模板定义明确匹配并包含在内。 给定的偏特化

    template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
    

    实际上是在定义is_default_constructable&lt;int, int&gt;,它与请求的is_default_constructable&lt;int, void&gt; 不匹配,因此即使替换成功,也会忽略特化。 这使得主定义(继承 false_type)成为唯一可行的定义,因此被选中。

    当专业化有wrap&lt;&gt;(或std::void_t&lt;&gt;)将第二个参数强制为void 时,专业化定义了与请求匹配的is_default_constructable&lt;int, void&gt;。这个定义(假设替换成功,即T() 格式正确)比主要定义(根据排序特殊化的超级复杂规则)更专业,因此被选中。

    顺便说一句,当 T 是引用类型或其他极端情况时,上述幼稚的实现可能无法按预期工作,这是使用所有这些的标准库版本的一个很好的理由。他们标准委员会的人比我聪明得多,并且已经想到了所有这些事情。

    This answerthis answer 对有些相关的问题有更详细的信息让我正确。

    而且,是的,我不能拼写constructible,假设那是一个词。这是使用标准库的另一个好理由。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多