为什么 enable_if<>* = nullptr 有效而 enable_if<> = void 无效?

Why does enable_if<>* = nullptr work when enable_if<> = void doesn't?

基本问题陈述

我正在学习 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;
}

编译器抱怨 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 = ...),它就无法编译。

如果有更熟悉 TMP 和 enable_if 的人向我解释一下,我将不胜感激:

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

非常感谢。

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

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

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

当它失败时,它根本不是一个参数。

与 nullptr 情况等价的是:

std::enable_if_t<std::is_base_of_v<Base, T>, bool> = true

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

bool = true

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


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

对于 enable = value 案例,我们正在尝试基于 如果 存在模板非类型参数的 SFINAE。失败时,没有签名可以比较,所以不能碰撞。

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

现在,这在概念版中已经过时了,所以不要迷恋语法。