enable_if

有条件地为 SFINAE 重载决策设置类型的实例。 当且仅当 enable_if<Condition,Type>::typeType 时,嵌套的 typedef Condition 才存在(并且是 true 的同义词)。

语法

template <bool B, class T = void>
struct enable_if;

参数

B
确定存在产生的类型的值。

T
Btrue 时要实例化的类型。

注解

如果 Btrue,则 enable_if<B, T> 具有名为“type”的嵌套 typedef(它是 T 的同义词)。

如果 Bfalse,则 enable_if<B, T> 不具有名为“type”的嵌套 typedef。

提供此别名模板:

template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;

在 C++ 中,模板参数的替换失败不是其本身的错误,这称为 SFINAE(替换失败不是错误)。 通常,enable_if 用于从重载决策中删除候选项(即剔除重载集),以便为了支持一个定义而拒绝另一个定义。 这符合 SFINAE 行为。 有关 SFINAE 的详细信息,请参阅 Wikipedia 上的替换失败不是错误

以下是四种示例方案:

  • 方案 1:包装函数的返回类型:
    template <your_stuff>
typename enable_if<your_condition, your_return_type>::type
    yourfunction(args) {// ...
}
// The alias template makes it more concise:
    template <your_stuff>
enable_if_t<your_condition, your_return_type>
yourfunction(args) {// ...
}
  • 方案 2:添加具有默认参数的函数参数:
    template <your_stuff>
your_return_type_if_present
    yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}
  • 方案 3:添加具有默认参数的模板参数:
    template <your_stuff, typename Dummy = enable_if_t<your_condition>>
rest_of_function_declaration_goes_here
  • 方案 4:如果你的函数具有非模板化的参数,你可以包装其类型:
    template <typename T>
void your_function(const T& t,
    enable_if_t<is_something<T>::value, const string&>
s) {// ...
}

方案 1 不能使用构造函数和转换运算符,因为它们没有返回类型。

方案 2 可使参数处于未命名状态。 你可以假定 ::type Dummy = BAR,但与名称 Dummy 无关,而且向其提供一个名称很可能会触发“未引用的参数”警告。 你必须选择 FOO 函数参数类型和 BAR 默认参数。 你可以假定 int0,但随后你的代码用户可能会不小心将被忽略的多余整数传递给函数。 因此,我们建议你使用 void **0nullptr,因为它们几乎都无法转换为 void **

template <your_stuff>
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}

方案 2 也适用于普通构造函数。 但是,它不适用于转换运算符,因为它们无法采用多余的参数。 它也不适用于 variadic 构造函数,因为添加多余的参数会使函数参数打包非导出上下文,从而与使用 enable_if 的初衷背道而驰。

方案 3 可使用名称 Dummy,但是它是可选的。 仅“typename = typename”可用,但是如果你认为它看起来太怪异,则可以使用“虚拟”名,只是不要使用还可能会在函数定义中使用的名称。 如果你未向 enable_if 提供类型,则它默认为 void,这是完全合理的,因为你不在乎 Dummy 的具体内容。 这适用于所有情况,包括转换运算符和 variadic 构造函数。

方案 4 在不具有返回类型的构造函数中可用,因此可解决方案 1 的包装限制问题。 但是,方案 4 受限于并非始终可用的非模板化的函数参数。 (在模板化的函数参数上使用方案 4 可以阻止模板参数导入在其上运行。)

enable_if 功能强大,但是如果误用也会很危险。 因为其用途是使候选项在重载决策之前消失,如果误用它,产生的结果可能很容易让人产生混淆。 以下是一些建议:

  • 编译时,请不要使用 enable_if 在实现之间作出选择。 请永远都不要为 enable_if 编写一个 CONDITION 并为 !CONDITION 编写另一个 enable_if。 请改为使用标记调度模式,例如,一种根据所提供的迭代器的优点选择实现的算法。

  • 请不要使用 enable_if 强制执行要求。 如果要验证模板参数,并且如果验证失败,引发错误,而未选择其他实现,请使用 static_assert

  • 如果你拥有会使其他好的代码变得不明确的重载集,则请使用 enable_if。 通常,这种情况发生在隐式转换构造函数中。

示例

此示例说明了 C++ 标准库模板函数 std::make_pair() 如何利用 enable_if

void func(const pair<int, int>&);

void func(const pair<string, string>&);

func(make_pair("foo", "bar"));

在本示例中,make_pair("foo", "bar") 将返回 pair<const char *, const char *>。 重载决策必须确定你想要的 func()pair<A, B> 具有 pair<X, Y> 中的隐式转换构造函数。 这不是新内容,它在 C++98 中出现过。 但是,在 C++98/03 中,隐式转换构造函数的签名始终存在,即使它是 pair<int, int>(const pair<const char *, const char *>&) 也是如此。 重载决策不介意实例化该构造函数的企图严重瓦解,因为 const char * 不可隐式转换为 int;在实例化函数定义之前,它只是在查看签名。 因此,示例代码是不明确的,因为存在可将 pair<const char *, const char *> 转换为 pair<int, int>pair<string, string> 的签名。

C++11 通过使用 enable_if 确保const X& 可隐式转换为 Aconst Y& 可隐式转换为 Bpair<A, B>(const pair<X, Y>&) 才存在,来解决此多义性问题。 这允许重载决策确定 pair<const char *, const char *> 不可转换为 pair<int, int>,以及采用 pair<string, string> 的重载可行。

要求

标头<type_traits>

命名空间std

另请参阅

<type_traits>