具有默认参数的模板特化

4
我有一个程序,如下所示。有一个基本模板struct X和使用SFINAE的部分特化。
template <typename T, typename U = void>
struct X{
  X() {
    std::cout << "in 1" << std::endl;
  };
};

template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>> > {
  X() {
    std::cout << "in 2" << std::endl;
  };
};

int main() {
  X<int> x;
}

运行程序后会输出in 2
1.为什么第二个特化被选择而不是第一个,因为它们都有效地声明了一个struct X<int, void>。是什么使得std::enable_if_t<std::is_integral_v<T>>比基本模板中显示的默认模板类型参数更加专业化?
2.为什么基本模板的默认类型参数必须与部分特化定义的类型相同才能调用部分特化并输出in 2?为什么将其更改为std::enable_if_t<std::is_integral_v<T>, bool>会导致调用基本模板in 1

你不需要std::enable_if_t来问这个问题。只需在第一个示例中定义偏特化为template <typename T> struct X<T, void>,在第二个示例中使用bool而不是void即可。 - Daniel Langr
2个回答

7
你的问题的答案在于 模板偏序。这是编译器用来确定哪个模板最合适(无论是函数模板重载还是类模板特化)的机制。
简而言之,你的通用模板实现有两个参数 T 和 U,而你的 SFINAE 特化只有 T 参数,第二个参数从 T 推导。因此,它比一般情况更加特化,最终当你引用 X 时,将会选择特化。
现在到了第二个问题。假设我们用 bool 替换 enable_if 参数而不是 void。现在我们的特化将变成 X 而不是 X,因此当你引用 X,即 X 时,它不再匹配特化,因为它们是两种不同的类型。

1

1) [...] std::enable_if_t>相比于基础模板中的默认模板类型参数更加专业化,是什么原因导致这种情况?

你知道吗,如果两个模板匹配,那么会选择更专业的一个。

嗯...第二个更专业,因为如果X匹配了特化版本(也就是X是整数类型),那么它也会匹配通用版本。

但是存在一些类型对(例如:std::stringvoid),它们匹配通用版本但不匹配特化版本。

因此,特化版本比通用版本更加专业化,所以在两个模板都匹配时,特化版本更受青睐。

为什么基础模板的默认类型参数必须与部分特化定义的类型相同才能调用部分特化,并打印2。为什么将其更改为std::enable_if_t,bool>会导致调用1中的基础模板?

您需要了解默认类型值技巧的工作原理。

您有一个通用版本,即

template <typename T, typename U = void>
struct X;

当你写 X<int> x 时,实际上你是在写 X<int, void> x;,这与泛型版本完全匹配。

特化版本为

template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>>>;

问题: X<int, void> 是否匹配 X< T, std::enable_if_t<std::is_integral_v<T>>>
回答:是的,因为 int 是整型,所以 std::enable_if_t<std::is_integral_v<T>> 被替换为 void
现在假设通用专业化变成
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>, bool>>

我们有一个 X<int> x,它再次是 X<int, void> x 并再次匹配泛型版本。
问题: X<int, void> 是否也匹配 X< T, std::enable_if_t<std::is_integral_v<T>, bool>>
答案:不,因为 std::enable_if_t<std::is_integral_v<T>, bool> 变成了 bool,而 X<int, void> 不匹配 X<int, bool>
因此,泛型版本是唯一匹配并被选中的版本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接