为什么 `SFINAE` (std::enable_if) 使用 bool 文字而不是 `true_t` / `false_t` 标记类?

Pat*_*ykB 1 c++ sfinae

我正在尝试了解SFINAE(我正在关注本教程),但有一些......“设计选择”我不理解,因此,我发现它们令人困惑。

假设我遇到这样的情况(包括重新实现std::enable_ifis there 只是为了演示我是如何理解的enable_if

// A default class (class type) declaration. Nothing unusual.
template <bool, typename T = void>
struct enable_if
{}; 

// A specialisation for <true, T> case. I understand 'why-s' of this. 
// -- 'why-s': if I attempt to access 'enable_if<false, T>::type' (which does not exist) I will get a substitution failure and compiler will just "move-on" trying to match "other cases".
template <typename T>
struct enable_if<true, T> {
    typedef T type;
};

// Here lies my problem:
template <class T,
         typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
Run Code Online (Sandbox Code Playgroud)


(1)我遇到的第一个问题是bool字面意思 ( true/ false)。我知道它们是正确的,模板可以接受原始数据类型(普通旧数据类型)的编译时常量值,但如果我的任务是设计“机制enable_if”而不是使用true/false我会创建一个标记类true_t(或True)和false_t(或False)如下:

class true_t {}; // or True

class false_t {}; // or False

template<typename T>
class is_integral // just to have "something" to use with "enable_if"
{
    using result = false_t;
};

template<>
class is_integral<int32_t> // same with all other int types
{
    using result = true_t;
};

template <typename B, typename T = void>
struct enable_if
{}; 

template <typename T>
struct enable_if<true_t, T>
{
    using type = T;
};
Run Code Online (Sandbox Code Playgroud)


(2)我发现多余的第二件事是需要指定typename T模板参数。enable_if按如下方式实现不是更容易/更好吗:

template <typename B>
struct enable_if
{}; 

template <>
struct enable_if<true_t>
{
    using type = void; // the 'type' exists therefore substitution failure will not occur. 
};
Run Code Online (Sandbox Code Playgroud)


我很清楚我的所有主张都远远不如当前现有的解决方案SFINAE,但我不明白为什么......我削减了当前功能(重要功能)的哪一部分?(连自己都没有意识到……)


我知道,在这个网站上,我有义务以......单个“问题帖子式”格式提出一个问题,但如果您认为可以接受,我也可以问一下这个语法是什么:

std::enable_if</* ... */>::type* = nullptr
Run Code Online (Sandbox Code Playgroud)

完成?现在超出​​了我的理解范围...

Nat*_*ica 5

我遇到的第一件事就是bool字面意思(true/ false)。我知道它们是正确的,模板可以接受原始数据类型(普通旧数据类型)的编译时常量值,但如果我的任务是设计“机制enable_if”而不是使用true/false我会创建一个标记类true_t(或True)和false_t(或False)如下

使用标记类型而不仅仅是 bool 的问题是您必须为代码添加额外的复杂性。如果您想检查编译时条件,例如sizeof,您不能只执行sizeof(T) == 8. 您必须进行抽象来执行检查并返回适当的标记类型。

我发现多余的第二件事是需要指定typename T模板参数。enable_if按如下方式实施不是更容易/更好吗

并不真地。如果您想使用 SFINAE 作为返回类型怎么办?那么你只能有一个 void 函数,这是不必要的限制。相反,您可以使用后来在 C++14 和 C++17 中添加的内容并创建别名。这使得名称不相关,并允许您删除typename

template< bool B, class T = void >
using enable_if_t = typename enable_if<B,T>::type;
template< class T >
inline constexpr bool is_integral_v = is_integral<T>::value;
Run Code Online (Sandbox Code Playgroud)

这允许您重写

template <class T,
         typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
Run Code Online (Sandbox Code Playgroud)

template <class T,
         std::enable_if_t<std::is_integral_v<T>,T>* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
Run Code Online (Sandbox Code Playgroud)

虽然我更喜欢使用 bool 来表示enable_if_t类似的类型

template <class T,
         std::enable_if_t<std::is_integral_v<T>, bool> = true>
void do_stuff(T& t) { /* do stuff */ };
Run Code Online (Sandbox Code Playgroud)

我知道,在这个网站上,我有义务以......单个“问题帖子式”格式提出一个问题,但如果您认为可以接受,我也可以问一下这个语法是什么:

std::enable_if</* ... */>::type* = nullptr
Run Code Online (Sandbox Code Playgroud)

完成?

它创建一个指向“返回”类型的指针std::enable_if并将其设置为空指针。这里的目标是创建一个仅在条件为真时才存在的模板参数。你可以将其重写为

typename = typename std::enable_if</* ... */>::type
Run Code Online (Sandbox Code Playgroud)

因此,您拥有类型参数,而不是非类型参数。它们都完成相同的事情,但后者无法重载不同的函数,enable_if因为默认模板参数不是签名的一部分。使用非类型参数的第一个版本包含在函数签名中,并且允许您重载enable_if's。