C++ Academy · 课时

enable_if 模式

以经典方式约束模板

第 4 / 4 课13 个步骤

enable_if 模式 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。

什么是 enable_if

std::enable_if 是一个小型特征,可以有条件地定义 type 成员。条件为真时该类型存在;条件为假时,外围模板会通过 SFINAE 被移除。

  • 位于 <type_traits> 中。
  • 它是约束模板的经典工具。
#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
doubleIt(T v) { return v * 2; }

int main() {
    std::cout << doubleIt(21) << '\n';
    return 0;
}

工作原理

只有当 Cond 为真时,enable_if<Cond, T>::type 才是 T。如果条件为假,就不存在 type 成员,因此替换会失败。

#include <iostream>
#include <type_traits>

int main() {
    std::cout << std::boolalpha;
    std::cout << std::is_same<std::enable_if<true, int>::type, int>::value << '\n';
    return 0;
}

返回类型中的 enable_if

最常见的放置位置是返回类型。只有条件成立时,函数才会存在。

#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
half(T v) { return v / 2; }

int main() {
    std::cout << half(5.0) << '\n';
    return 0;
}

作为模板参数的 enable_if

另一种惯用写法是将 enable_if 放入带默认值的模板参数中,从而保持返回类型简洁。

#include <iostream>
#include <type_traits>

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
T triple(T v) { return v * 3; }

int main() {
    std::cout << triple(4) << '\n';
    return 0;
}

enable_if_t 快捷写法

C++14 增加了别名 std::enable_if_t<Cond, T>,这样您可以省略 typename ...::type 这一冗长写法。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
negate(T v) { return -v; }

int main() {
    std::cout << negate(8) << '\n';
    return 0;
}

配对两个重载

提供互补的条件,使得对于任何类型恰好只有一个重载有效,并且不会产生歧义。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, const char*>
category() { return "integer"; }

template <typename T>
std::enable_if_t<!std::is_integral<T>::value, const char*>
category() { return "non-integer"; }

int main() {
    std::cout << category<int>() << ' ' << category<double>() << '\n';
    return 0;
}

组合条件

对特征值使用逻辑运算符来表达更丰富的约束,例如“整数类型且带符号”。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, T>
magnitude(T v) { return v < 0 ? -v : v; }

int main() {
    std::cout << magnitude(-12) << '\n';
    return 0;
}

约束构造函数

您可以将 enable_if 放在构造函数的模板参数上,使其仅对兼容类型可用。

#include <iostream>
#include <type_traits>

struct Wrapper {
    int value;
    template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    Wrapper(T v) : value(static_cast<int>(v)) {}
};

int main() {
    Wrapper w(42);
    std::cout << w.value << '\n';
    return 0;
}

默认模板参数陷阱

如果两个重载的区别仅在于带默认值的 enable_if 模板参数,它们会被视为重复定义。请使用不同的形式,例如将其中一个条件放在返回类型上。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, void>
printKind() { std::cout << "int\n"; }

template <typename T>
std::enable_if_t<std::is_floating_point<T>::value, void>
printKind() { std::cout << "float\n"; }

int main() {
    printKind<int>();
    printKind<double>();
    return 0;
}

与概念比较

C++20 的概念用清晰得多的语法取代了大多数 enable_if 用法,但对于 C++20 之前的代码和某些边界情况,enable_if 仍然不可或缺。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T>
square(T v) { return v * v; }

int main() {
    std::cout << square(6) << ' ' << square(1.5) << '\n';
    return 0;
}

完整示例

将所有内容结合起来:在编译期选择一个函数族,以不同方式格式化整数和浮点数。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, void>
format(T v) { std::cout << "int[" << v << "]\n"; }

template <typename T>
std::enable_if_t<std::is_floating_point<T>::value, void>
format(T v) { std::cout << "float(" << v << ")\n"; }

int main() {
    format(10);
    format(3.5);
    return 0;
}

快速检查

测试您对 enable_if 的理解。

回顾

您学习了经典的 enable_if 模式:

  • 仅当条件为真时定义 ::type,从而启用 SFINAE
  • 放在返回类型或带默认值的模板参数中
  • 使用 enable_if_t 别名,并通过逻辑运算符组合特征
  • C++20 中的概念取代了它,但它仍被广泛使用

至此,模板特化与 SFINAE 课程全部完成。

免费开始

用 AI 导师学习 C++ — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「enable_if 模式」课时是免费的吗?

是的 — 「enable_if 模式」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。

「enable_if 模式」这节课中我会学到什么?

以经典方式约束模板 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C++ Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「enable_if 模式」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C++ Academy 课中编写并运行代码吗?

能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 完全特化
  2. 部分特化
  3. SFINAE
  4. enable_if 模式
← 返回 C++ Academy