为什么 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
的人向我解释一下,我将不胜感激:
- 为什么将
enable_if
声明为指向类型的指针并将其默认为 nullptr
有效?
- 默认的语义规则是什么
enable_if
?
enable_if
产生的命名类型的语义规则是什么?
- 是否有我可以使用的参考资料,它清楚地解释了模板领域中的这个规则和其他类似的规则?
非常感谢。
第一组变体你只是在设置模板类型参数的值。两个具有不同模板类型参数值的重载发生冲突,因为它们都是同类 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。失败时,没有签名可以比较,所以不能碰撞。
剩下的就是让语法简单漂亮。
现在,这在概念版中已经过时了,所以不要迷恋语法。
基本问题陈述
我正在学习 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
的人向我解释一下,我将不胜感激:
- 为什么将
enable_if
声明为指向类型的指针并将其默认为nullptr
有效? - 默认的语义规则是什么
enable_if
? enable_if
产生的命名类型的语义规则是什么?- 是否有我可以使用的参考资料,它清楚地解释了模板领域中的这个规则和其他类似的规则?
非常感谢。
第一组变体你只是在设置模板类型参数的值。两个具有不同模板类型参数值的重载发生冲突,因为它们都是同类 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。失败时,没有签名可以比较,所以不能碰撞。
剩下的就是让语法简单漂亮。
现在,这在概念版中已经过时了,所以不要迷恋语法。