C++ 部分专业化和std的需要:void\u t<&燃气轮机;

C++ 部分专业化和std的需要:void\u t<&燃气轮机;,c++,language-lawyer,partial-specialization,C++,Language Lawyer,Partial Specialization,一个是语言律师 我正在和SFINAE和TMP打交道,试图获得更深入的理解 考虑下面的代码,std::的一个简单实现是可构造的 #include <type_traits> template <typename T, typename = void> struct is_default_constructable : std::false_type {}; template <typename T> struct is_default_constructabl

一个是语言律师

我正在和SFINAE和TMP打交道,试图获得更深入的理解

考虑下面的代码,std::的一个简单实现是可构造的

#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;
}
(即,将第二个模板参数包装在
std::void\u t
的实体模型中,强制第二个类型为
void
),这将按预期工作

更奇怪的是,在主模板中使用
void
以外的类型作为默认类型或
wrap
作为默认类型的方案的变体也会失败,除非这两种类型相同

有人能解释一下为什么
wrap
的类型和第二个模板参数default类型需要相同才能选择专门化吗


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

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

因此,在上述情况下,表示
是默认的\u可构造的
的代码实际上是在应用默认的第二个参数后请求实例化模板
是默认的\u可构造的
。然后考虑可能的定义

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

template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
模板结构是\u默认\u可构造的:std::true\u类型{};
实际上定义了与请求的
is\u default\u constructable
不匹配的
is\u default\u constructable
,因此即使替换成功,也会忽略专门化。 这使得主定义(继承false_类型)成为唯一可行的定义,因此选择它

当专门化使用
换行
(或
std::void\u t
)强制第二个参数为
void
,则专门化将定义与请求匹配的
是可构造的。此定义(假设替换成功,即
T()
格式正确)比主定义更专业(根据排序专业化的超复杂规则),因此选择它

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

对于一些相关的问题,我有更详细的信息来帮助我纠正错误

是的,我不会拼写constructible,假设这是一个单词。这是使用标准库的另一个很好的理由

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