enable_if class 参数不是 void 的模板特化

enable_if for class template specialization with argument other than void

我知道 C++ 编译器优先选择模板特化而不是主模板:

template<class T, class Enable = void>
class A {}; // primary template

template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, void>> {
}; // specialization for floating point types

但是,我不明白为什么当 void 以外的类型用作 enable_if 的参数时选择失败:

template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
}; // specialization for floating point types

编译器肯定会“看到”class A<T, int>。 所以肯定不可能是SFINAE,不过主模板比专业模板更受欢迎。

这个问题源于可以使用自定义类型检测机制而不是 enable_if 的上下文,例如SFINAE 友好型提取器,如 common_type.

当你有

A<some_floating_point_type> some_name;

模板参数为some_floating_point_type和隐式void。当编译器实例化时

template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
}; // specialization for floating point types

它会得到 A<some_floating_point_type, int>,这与 some_nameA<some_floating_point_type, void> 类型不匹配。因此,专业化将被忽略,您将获得主模板。您可以通过尝试创建一个 A<some_floating_point_type, int> 来验证这一点,您将看到专业化已被选中。

我发现将专业化视为替代方案很有帮助。首先推导模板参数,然后如果它们匹配任何特化,然后我们切换到使用该替代配方。如果没有,则使用原始配方。

在编译器知道它将用于 primary 模板的类型之前,特化是无关紧要的。

当你写 A<double> 时,编译器只会查看主模板并发现你实际上是指 A<double,void>

只有 然后 它正在寻找专业化。现在,如果你的专业是 A<double,int>,那么它就不适合了,因为你要求的是 A<double,void>.

简单来说,这

template<class T, class Enable = void>
class A {};

表示“A 是一个模板,它有两个参数,第二个参数默认为 void”。那是主要模板。当您显式仅提供一个参数时,第二个参数为 void.

现在专精:

template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
};

这不会改变以上内容。 A 仍然是一个有两个参数的模板,如果没有指定第二个参数,那么它就是 void。简而言之,当条件为 false 时,它意味着“在 std::enable_if_t<std::is_floating_point_v<T> 中替换 T 是失败的,因为类型别名不存在”,SFINAE 启动并忽略专业化。当条件为 true 时:“每当使用参数 T(某种类型)和 int 实例化 A 时,然后使用此定义”。

当您实例化 A<int> 时,第二个模板参数是 void。专业化不会改变这一点。即 A<int> 实际上是 A<int,void>。与专业化不符。