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

时间:2019-08-03 15:57:28

标签: c++ templates c++17 enable-if

基本问题说明

我正在学习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. 有没有我可以使用的参考资料,可以在模板范围内清楚地说明此规则以及其他类似规则?

非常感谢。

1 个答案:

答案 0 :(得分:3)

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

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

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

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

等效于nullptr casebis:

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

当子句为true时,enable if的值为bool,我们得到:

bool = true

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


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

enable = value情况下,我们正在尝试基于 的SFINAE,那里有一个模板非类型参数。失败时,没有要比较的签名,因此不会冲突。

剩下的是使语法简单漂亮。

现在,所有这些都已被Concepts过时了,所以不要爱上语法。