当 enable_if<> = void 不起作用时,为什么 enable_if<>* = nullptr 起作用?

ten*_*jed 6 c++ templates enable-if c++17

基本问题陈述

我正在学习 SFINAE。我尝试了一个非常简单的enable_if

// 1: A foo() that accepts arguments that are derived from Base
template <typename T, typename Enable = enable_if_t<std::is_base_of_v<Base, T>>>
void foo(T thing) {
    std::cout << "It's a derived!" << std::endl;
}

// 2: A foo() that accepts all other arguments
template <typename T, typename Enable = enable_if_t<!std::is_base_of_v<Base, T>>>
void foo(T thing) {
    std::cout << "It's not a derived." << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨说foo是多重定义的。互联网告诉我,这是因为检查函数签名时模板参数不相关。

我试过的变体

为了尽可能进行最基本的元编程,我开始用语法来解决这个问题。这是我为该enable_if语句尝试的内容列表(倒置语句,即!std::is_base_of相同,但为简洁起见省略了):

匿名类型,否typename,等于0

https://en.cppreference.com/w/cpp/types/enable_if告诉我我上面做的事情是错误的。但是它的建议(在第一个注释块下找到)是适当的神秘,更重要的是,也无法编译。

std::enable_if_t<std::is_base_of_v<Base, T>> = 0

匿名类型,否typename,等于void

考虑到如果我正在使用类型进行编程,使用类型将是一个明智的选择,我反而尝试将模板默认为void. 没有骰子。

std::enable_if_t<std::is_base_of_v<Base, T>> = void

匿名类型,是typename,等于void

当我们在上面抛出语法时,如果我将此模板参数默认为一个类型,我不应该使用 typename 关键字吗?

typename std::enable_if_t<std::is_base_of_v<Base, T>> = void

最后,哦,显然是有效的

typename enable_if_t<std::is_base_of_v<Base, T>, T>* = nullptr

我已经问过每个我知道为什么这有效但我的其他变体却没有的人,他们同样感到困惑。我不知所措。更令人困惑的是,如果我将这种类型命名为(例如typename Enable = ...),它将无法编译。

如果有一位更熟悉 TMPenable_if并向我解释的人,我将不胜感激:

  1. 为什么将 声明enable_if为指向类型的指针并默认它nullptr起作用?
  2. 违约的语义规则是enable_if什么?
  3. 产生的命名类型的语义规则是enable_if什么?
  4. 有没有我可以使用的参考资料,它清楚地解释了模板领域中的这个规则和其他类似的规则?

非常感谢。

Yak*_*ont 6

第一组变体只是设置模板类型参数的值。模板类型参数的两个具有不同值的重载会发生冲突,因为它们都是同类template<class,class>并且具有相同的函数参数。

非类型模板参数情况,如果您最终拥有类型为 void 的模板非类型参数,则使用原始启用的情况。那是非法的;各种错误信息是非法的各种方式。

当您添加星号时,当 enable if 子句通过时,它是 void 指针类型的模板非类型参数。

当它失败时,它根本不是一个争论。

相当于 nullptr 的情况是:

std::enable_if_t<std::is_base_of_v<Base, T>, bool> = true
Run Code Online (Sandbox Code Playgroud)

当子句为真时, enable if 计算为bool,我们得到:

bool = true
Run Code Online (Sandbox Code Playgroud)

一个默认为 true 的 bool 类型的模板非类型参数。当子句(子句的基础)为假时,我们得到 SFINAE 失败;那里没有模板类型或非类型参数。


对于这些class Whatever = enable_if案例,我们正在尝试基于模板参数的默认值的 SFINAE。这会导致签名冲突,因为如果在重载解决期间(在同一阶段)找到签名,则签名必须是唯一的。

随着enable = value情况下,我们是基于试图SFINAE如果有一个模板非类型参数存在。失败时,没有要比较的签名,因此不会发生冲突。

剩下的就是让语法简单漂亮。

现在,概念已经过时了,所以不要爱上语法。