条件编译->enable_if和 if constexpr使用区别

news2024/12/16 20:29:32

enable_if 和 if constexpr 是 C++ 中用于控制编译或运行时条件的重要工具,它们各有不同的用途和使用场景。以下是它们的主要区别:

1. enable_if

std::enable_if 是一个类型特征,用于在编译时根据条件选择类型。常用于模板元编程,以使某些模板在特定条件下启用或禁用。

示例:

#include <type_traits>
#include <iostream>

template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
print(T value) {
    std::cout << "Integral: " << value << std::endl;
}

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type
print(T value) {
    std::cout << "Floating point: " << value << std::endl;
}

在这个例子中,只有当 T 是整型或浮点型时,print 函数才会被编译。

2. if constexpr

if constexpr 是 C++17 引入的一种条件语句,用于在编译时根据条件选择性地编译代码块。这允许你在编译时根据条件跳过某些语句,而不需要使用 SFINAE(Substitution Failure Is Not An Error)。

示例:

#include <iostream>
#include <type_traits>

template <typename T>
void print(T value) {
    if constexpr (std::is_integral<T>::value) {
        std::cout << "Integral: " << value << std::endl;
    } else if constexpr (std::is_floating_point<T>::value) {
        std::cout << "Floating point: " << value << std::endl;
    }
}

在这个例子中,if constexpr 检查 T 的类型,并只编译相应的代码块。与 enable_if 不同,它不需要使用复杂的模板结构。

总结

  • enable_if: 在类或函数模板的定义上决定能否实例化。这通常用于模板特化。
  • if constexpr: 在函数体内的条件编译,根据条件编译某些代码块。

        在选择时,如果想要基于类型启用或禁用功能,选择 enable_if;如果你需要在函数内部进行条件逻辑,使用 if constexpr


  if constexpr 是 C++17 中引入的一种条件编译的语法,它允许在编译时根据某些条件选择代码的路径。这种特性使得代码可以根据类型或模板参数在编译时进行选择,从而避免了在运行时进行不必要的判断和决策。

基本用法

if constexpr 语句的基本语法如下:

if constexpr (条件) {
    // 如果条件为真,这部分代码会被编译
} else {
    // 如果条件为假,这部分代码会被编译
}

编译时条件

if constexpr 的条件必须是一个在编译期可求值的常量表达式。如果条件为真,则包含在 if 块中的代码会被编译,else 块中的代码将被忽略;反之亦然。

示例

这里是一个简单的示例,展示了如何使用 if constexpr 来根据类型选择不同的逻辑:

#include <iostream>
#include <type_traits>

template <typename T>
void process(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "Processing an integral type: " << value << std::endl;
    } else {
        std::cout << "Processing a non-integral type: " << value << std::endl;
    }
}

int main() {
    process(10);      // 输出: Processing an integral type: 10
    process(10.5);   // 输出: Processing a non-integral type: 10.5
    return 0;
}

在这个示例中,process 函数的行为会根据输入参数的类型而有所不同。如果传入的类型是整数类型,输出相关消息;否则,输出非整数的处理信息。

优势

  1. 性能:由于 if constexpr 在编译时进行条件选择,不会对运行时性能产生影响。
  2. 避免冗余代码:通过使用 if constexpr 可以避免处理不必要的代码路径,减少了模板代码的复杂性。
  3. 类型安全:编译器会对每个分支进行静态检查,只有有效的代码才能被编译。

if constexpr 在 C++17 及之后的版本中提供了更强大的编译时编程能力,让开发者能够以更清晰和优雅的方式编写模板代码。

在 C++11 中,并没有 if constexpr 这个特性。if constexpr 是 C++17 中引入的,用于在编译时进行条件编译。然而,在 C++11 中,你可以使用一些其他的技巧来实现类似的功能。通常,这通过模板特化、SFINAE(Substitution Failure Is Not An Error,替换失败不产生错误)或 std::enable_if 来实现。

以下是一些示例,展示如何在 C++11 中实现条件编译的效果:

1. 使用模板特化

可以通过模板特化来实现基于类型的条件逻辑:

#include <iostream>
#include <type_traits>

// 基础模板
template <typename T>
struct Processor;

// 整数特化
template <>
struct Processor<int> {
    static void process(int value) {
        std::cout << "Processing an integral type: " << value << std::endl;
    }
};

// 非整数特化
template <>
struct Processor<double> {
    static void process(double value) {
        std::cout << "Processing a non-integral type: " << value << std::endl;
    }
};

int main() {
    Processor<int>::process(10);      // 输出: Processing an integral type: 10
    Processor<double>::process(10.5); // 输出: Processing a non-integral type: 10.5
    return 0;
}

2. 使用 std::enable_if

std::enable_if 可以用于实现 SFINAE 特性,以选择合适的函数版本。这样可以模仿 if constexpr 的行为:

#include <iostream>
#include <type_traits>

// 函数模板的两个版本,根据类型进行选择
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type 
process(T value) {
    std::cout << "Processing an integral type: " << value << std::endl;
}

template <typename T>
typename std::enable_if<!std::is_integral<T>::value>::type 
process(T value) {
    std::cout << "Processing a non-integral type: " << value << std::endl;
}

int main() {
    process(10);      // 输出: Processing an integral type: 10
    process(10.5);   // 输出: Processing a non-integral type: 10.5
    return 0;
}

3. 使用 static_assert

对于一些特定条件,你可以使用 static_assert 来在编译时进行条件检查并产生错误。在 C++11 中,可以通过这种方法来确保某些条件满足:

#include <iostream>
#include <type_traits>

template <typename T>
void process(T value) {
    static_assert(std::is_arithmetic<T>::value, "T must be an arithmetic type");
    if (std::is_integral<T>::value) {
        std::cout << "Processing an integral type: " << value << std::endl;
    } else {
        std::cout << "Processing a non-integral type: " << value << std::endl;
    }
}

int main() {
    process(10);      // 输出: Processing an integral type: 10
    process(10.5);   // 输出: Processing a non-integral type: 10.5
    return 0;
}

总结

在 C++11 中,因为没有 if constexpr,所以我们依靠模板特化和 std::enable_if 等技巧来实现条件编译的效果。这并没有像在 C++17 中使用 if constexpr 那样简洁,但依然能够达到类似的功能。对于更复杂的类型选择或分支,可以通过组合这些技术来实现。


在 C++ 中,可以使用模板元编程和 SFINAE(Substitution Failure Is Not An Error)来根据部分模板条件编译多个模板。以下是几个常见的方法来实现这一目标。

1. 使用 std::enable_if

可以利用 std::enable_if 按条件选择特定的模板重载。

#include <iostream>
#include <type_traits>

// 主模板
template <typename T, typename Enable = void>
struct MyClass;

// 针对整型的特化
template <typename T>
struct MyClass<T, typename std::enable_if<std::is_integral<T>::value>::type> {
    void print() {
        std::cout << "Integral type\n";
    }
};

// 针对浮点型的特化
template <typename T>
struct MyClass<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
    void print() {
        std::cout << "Floating point type\n";
    }
};

int main() {
    MyClass<int> intObj;
    intObj.print();  // 输出: Integral type

    MyClass<double> doubleObj;
    doubleObj.print();  // 输出: Floating point type

    return 0;
}

2. 使用 if constexpr

C++17 提供的 if constexpr 允许在模板中基于条件编译不同的代码路径。

#include <iostream>
#include <type_traits>

template <typename T>
struct MyClass {
    void print() {
        if constexpr (std::is_integral<T>::value) {
            std::cout << "Integral type\n";
        } else if constexpr (std::is_floating_point<T>::value) {
            std::cout << "Floating point type\n";
        } else {
            std::cout << "Other type\n";
        }
    }
};

int main() {
    MyClass<int> intObj;
    intObj.print();  // 输出: Integral type

    MyClass<double> doubleObj;
    doubleObj.print();  // 输出: Floating point type

    MyClass<std::string> strObj;
    strObj.print();  // 输出: Other type

    return 0;
}

3. 使用部分特化

对于类模板,可以使用部分特化来根据条件编译不同部分。

#include <iostream>
#include <type_traits>

// 主模板
template <typename T>
class MyClass;

// 针对整型的特化
template <>
class MyClass<int> {
public:
    void print() {
        std::cout << "Specialized for int\n";
    }
};

// 针对浮点型的特化
template <>
class MyClass<double> {
public:
    void print() {
        std::cout << "Specialized for double\n";
    }
};

// 通用定义
template <typename T>
class MyClass {
public:
    void print() {
        std::cout << "Generic type\n";
    }
};

int main() {
    MyClass<int> intObj;
    intObj.print();  // 输出: Specialized for int

    MyClass<double> doubleObj;
    doubleObj.print();  // 输出: Specialized for double

    MyClass<std::string> strObj;
    strObj.print();  // 输出: Generic type

    return 0;
}

总结

  • 使用 std::enable_if: 适合于函数或类模板的重载,灵活且广泛使用。
  • 使用 if constexpr: 更简洁,适合 C++17 及以上版本,能直接在函数体根据条件进行编译。
  • 部分特化: 当需要特定类型的特殊行为时,适合用于类模板。

根据具体需求选择合适的方法来实现模板条件编译。


typename std::enable_if<std::is_integral<T>::value>::type 中value是什么?

.value

在 std::is_integral 中,value 是一个静态成员常量,它是一个布尔值(bool),指出模板参数 T 是否为整数类型。具体来说:

  • std::is_integral<T>::value 为 true,当 T 是整数类型时。
  • std::is_integral<T>::value 为 false,当 T 不是整数类型时。

示例

以下是一些示例,展示了 std::is_integral 和其 value 成员的作用:

#include <iostream>
#include <type_traits>

int main() {
    std::cout << std::boolalpha; // 输出 bool 值为 `true` 或 `false`

    // 检查不同类型
    std::cout << "int is integral: " << std::is_integral<int>::value << std::endl;               // true
    std::cout << "char is integral: " << std::is_integral<char>::value << std::endl;             // true
    std::cout << "float is integral: " << std::is_integral<float>::value << std::endl;           // false
    std::cout << "double is integral: " << std::is_integral<double>::value << std::endl;         // false
    std::cout << "std::string is integral: " << std::is_integral<std::string>::value << std::endl; // false

    return 0;
}

输出结果

运行上述代码将输出:

int is integral: true
char is integral: true
float is integral: false
double is integral: false
std::string is integral: false

结论

在使用 std::enable_if 时,std::is_integral<T>::value 的作用是:

  • 作为条件判断的依据,决定是否启用某个模板。
  • 让我们能够根据类型的特性(如是否为整数类型)对模板函数进行选择,实现条件编译的效果。

通过这种机制,我们可以更好地管理函数重载和模板类型的选择,使得代码更加灵活和类型安全。


结合std::is_same根据不同的结构体类型,进行条件编译

要根据不同的结构体类型进行条件编译,在 C++ 中可以利用模板特化和 std::enable_if 来实现。下面是一个简单的示例,展示了如何根据传入的结构体类型执行不同的功能。

示例代码

#include <iostream>
#include <type_traits>

// 定义一些结构体
struct A {
    int x;
};

struct B {
    double y;
};

struct C {
    std::string z;
};

// 主模板:默认行为
template <typename T, typename Enable = void>
struct Processor;

// 特化:处理结构体 A
template <typename T>
struct Processor<T, typename std::enable_if<std::is_same<T, A>::value>::type> {
    static void process(const T& obj) {
        std::cout << "Processing A with x = " << obj.x << std::endl;
    }
};

// 特化:处理结构体 B
template <typename T>
struct Processor<T, typename std::enable_if<std::is_same<T, B>::value>::type> {
    static void process(const T& obj) {
        std::cout << "Processing B with y = " << obj.y << std::endl;
    }
};

// 特化:处理结构体 C
template <typename T>
struct Processor<T, typename std::enable_if<std::is_same<T, C>::value>::type> {
    static void process(const T& obj) {
        std::cout << "Processing C with z = " << obj.z << std::endl;
    }
};

int main() {
    A a{10};
    B b{20.5};
    C c{"Hello"};

    Processor<A>::process(a); // 处理 A
    Processor<B>::process(b); // 处理 B
    Processor<C>::process(c); // 处理 C

    return 0;
}

程序解析

  1. 定义结构体:我们定义了三个简单结构体 AB 和 C,每个结构体包含一个属性。

  2. 模板结构Processor 是一个模板结构,用于处理不同类型的结构体。它的主模板默认不定义任何内容。

  3. 特化

    • 我们为每一个结构体类型 (AB 和 C) 创建了一个特化,使用 std::enable_if 和 std::is_same 来确定当前实例化的类型。
    • 通过 std::is_same<T, A>::value,我们可以判断传入的类型是否为 A,如果是,则实现该特化的 process 函数。
  4. 示例:在 main 函数中,我们创建了 AB 和 C 的实例,然后调用 Processor 的 process 函数。根据传入的结构体类型,程序会调用相应的 process 特化,进行处理。

输出

运行该程序将产生如下输出:

Processing A with x = 10
Processing B with y = 20.5
Processing C with z = Hello

结论

本示例展示了如何使用模板特化和 std::enable_if 来根据不同的结构体类型在编译时选择不同的执行路径。这种方法在需要根据类型执行特定逻辑时非常有用,并且可以提高代码的灵活性和可读性。同时,利用 std::is_same 检查类型使得代码更加简洁和安全。


多参数模板使用

std::enable_if通常用于控制模板的实例化。当特定的条件满足时,模板实例化,否则模板不可见。

可能有一个模板函数 add,它接受两个参数并返回它们的和。但是,我们只想当这两个参数都是整数时,这个函数才可用。我们可以使用std::enable_if来实现这个要求:

template <typename T, typename U,
          std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>, int> = 0>
T add(T a, U b) {
    return a + b;
}


综上

使用 if constexpr 的情况

if constexpr 是C++17中引入的一个特性,允许在编译时根据模板参数或运行时已知的类型特性来执行不同的代码路径。它允许你根据某个条件的真实性在编译时选择不同的实现,这些条件必须在编译时就能确定其值。这对于模板函数和类非常有用,可以在编译时减少代码生成的冗余和提高效率。例如:

template <typename T>
void foo(T t) {
    if constexpr (std::is_integral_v<T>) {
        // 执行整数的操作
    } else if constexpr (std::is_floating_point_v<T>) {
        // 执行浮点数的操作
    } else {
        // 处理其他类型
    }
}

在这个例子中,if constexpr 根据 T 的类型选择不同的代码路径。由于这些检查是在编译时进行的,所以不会产生运行时开销。这对于性能和代码大小优化特别有用。

使用 std::enable_if 的情况

std::enable_if 是模板元编程中的另一个强大工具,用于控制模板的可见性和实例化。它常用于SFINAE(Substitution Failure Is Not An Error)技术中,允许你根据某些条件来启用或禁用模板函数或类的特定版本。这对于创建依赖于类型的函数签名非常有用。例如:

template <typename T>
typename std::enable_if<std::is_integral<T>::value, int>::type 
foo(T t) {
    // 仅当 T 是整数类型时才可用的代码路径
}

在这个例子中,只有当 T 是整数类型时,foo 函数才会被实例化并变得可见。其他类型则无法看到该函数,因为 std::enable_if 会产生一个依赖于模板参数的类型别名,该别名仅在满足条件时存在。这使得你可以根据类型特性定制接口,而不必担心为不支持的类型提供实现。这对于创建通用代码和特化代码之间的灵活切换非常有用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2260680.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Studio新版本的一个资源id无法找到的bug解决

Android Studio新版本的一个资源id无法找到的bug解决 文章目录 Android Studio新版本的一个资源id无法找到的bug解决一、前言二、Android Studio的无法获取到资源id的bug1、一段简单的Java代码1、错误现象2、错误解决方法 三、其他1、小结2、gradle.properties文件 其他相关属性…

负载均衡器到底是个啥?

引言 目前在项目中&#xff0c;我们正在使用ELB&#xff08;弹性负载均衡&#xff09;&#xff0c;我希望能够完全理解其内部功能&#xff0c;因此写了这篇文章。 关于本文 这篇文章的最终目标是深刻理解“ELB是什么&#xff1f;”&#xff0c;并达到能够自信解释的程度。 …

远程调试软件对比与使用推荐

远程调试软件对比与使用推荐 远程调试是现代软件开发中不可或缺的一部分&#xff0c;尤其是在处理分布式系统、云端服务或远程服务器上的问题时。以下是对几种常见远程调试工具的详细对比和推荐使用场景。 1. GDB (GNU Debugger) 特点 开源&#xff1a;完全免费且开源&…

【unity】从零开始制作平台跳跃游戏--界面的认识,添加第一个角色!

在上一篇文章中&#xff0c;我们已经完成了unity的环境配置与安装⬇️ 【Unity】环境配置与安装-CSDN博客 接下来&#xff0c;让我们开始新建一个项目吧&#xff01; 新建项目 首先进入unityHub的项目页面&#xff0c;点击“新项目”&#xff1a; 我们这个系列将会以2D平台…

数据结构初阶---二叉树---堆

一、树 1.树的概念 树是一种非线性的数据结构&#xff0c;由n(n≥0)个有限结点组成的一个有层次关系的集合。形状类似一棵倒挂的树&#xff0c;根朝上&#xff0c;分支向下。 根结点没有前驱结点&#xff0c;可以有n(n≥0)个后继结点。 其余结点被分为M个互不相交的集合&am…

1. 机器学习基本知识(3)——机器学习的主要挑战

1.5 机器学习的主要挑战 1.5.1 训练数据不足 对于复杂问题而言&#xff0c;数据比算法更重要但中小型数据集仍然很普遍&#xff0c;获得额外的训练数据并不总是一件轻而易举或物美价廉的事情&#xff0c;所以暂时不要抛弃算法。 1.5.2 训练数据不具有代表性 采样偏差&#…

CentOS 上如何查看 SSH 服务使用的端口号?

我们知道&#xff0c;linux操作系统中的SSH默认情况下&#xff0c;端口是使用22&#xff0c;但是有些线上服务器并不是使用的默认端口&#xff0c;那么这个时候&#xff0c;我们应该如何快速知道SSH使用的哪个端口呢&#xff1f; 1、通过配置文件查看 cat /etc/ssh/sshd_confi…

【Java学习笔记】泛型

一、泛型的好处 好处&#xff1a;减少了类型转换的次数&#xff0c;提高了效率 二、泛型介绍 泛型&#xff1a;接受任何数据类型 的 数据类型 &#xff08;特别强调&#xff1a; E 具体的数据类型在定义 Person 对象的时候指定,即在编译期间&#xff0c;就确定 E 是什么类型…

benchANT (Time Series: Devops) 榜单数据解读

近日&#xff0c;国际权威数据库性能测试榜单 benchANT 更新了 Time Series: Devops&#xff08;时序数据库&#xff09;场景排名&#xff0c;KaiwuDB 数据库在 xsmall 和 small 两类规格下的时序数据写入吞吐、查询吞吐、查询延迟、成本效益等多项指标刷新榜单原有数据纪录 &a…

多进程并发跑程序:pytest-xdist记录

多进程并发跑程序&#xff1a;pytest-xdist记录 pytest -s E:\testXdist\test_dandu.py pytest -s testXdist\test_dandu.py pytest -s &#xff1a;是按用例顺序依次跑用例 pytest -vs -n auto E:\testXdist\test_dandu.py pytest -vs -n auto&#xff0c;auto表示以全部进程…

网络层IP协议(TCP)

IP协议&#xff1a; 在了解IP协议之前&#xff0c;我们市面上看到的"路由器"其实就是工作在网络层。如下图&#xff1a; 那么网络层中的IP协议究竟是如何发送数据包的呢&#xff1f; IP报头&#xff1a; IP协议的报头是比较复杂的&#xff0c;作为程序猿只需要我们重…

前端传入Grule,后端保存到 .grl 文件中

前端传入Grule&#xff0c;后端保存到 .grl 文件中 通过简单的输入框&#xff0c;将Grule的部分拆解成 规则名称 规则描述 规则优先级 规则条件 规则逻辑Grule关键字 when Then 模拟了 if 判断的条件和逻辑部分 类似于 shell 和 ruby 之类的脚本语言&#xff0c;有 then 关键字…

vlan和vlanif

文章目录 1、为什么会有vlan的存在2、vlan(虚拟局域网)1、vlan原理1. 为什么这样划分了2、如何实现不同交换机相同的vlan实现互访呢3、最优化的解决方法&#xff0c;vlan不同交换机4、vlan标签和vlan数据帧 5、vlan实现2、基于vlan的划分方式1、基于接口的vlan划分方式2、基于m…

遗传算法与深度学习实战(27)——进化卷积神经网络

遗传算法与深度学习实战&#xff08;27&#xff09;——进化卷积神经网络 0. 前言1. 自定义交叉算子2. 自定义突变操作符3. 进化卷积神经网络小结系列链接 0. 前言 DEAP toolbox 中提供的标准遗传操作符对于自定义的网络架构基因序列来说是不够的。这是因为任何标准的交叉算子…

react-dnd 拖拽事件与输入框的文本选中冲突

问题描述 当我们使用拖拽库的时候&#xff0c;往往会遇到拖拽的一个元素他的子孙元素有输入框类型的dom节点&#xff0c;当拖拽的事件绑定在该元素身上时候&#xff0c;发现子孙的输入框不能进行文本选中了&#xff0c;会按住鼠标去选中文本的时候会触发拖拽 实际的效果&…

经典NLP案例 | 推文评论情绪分析:从数据预处理到模型构建的全面指南

NLP经典案例&#xff1a;推文评论情绪提取 项目背景 “My ridiculous dog is amazing.” [sentiment: positive] 由于所有推文每秒都在传播&#xff0c;很难判断特定推文背后的情绪是否会影响一家公司或一个人的品牌&#xff0c;因为它的病毒式传播&#xff08;积极&#xff0…

杨振宁大学物理视频中黄色的字,c#写程序去掉(原版改进,三)

上一节&#xff0c;我们分清了主次矛盾&#xff0c;并搞定了主要矛盾&#xff08;去掉黄色的字&#xff09;&#xff0c;这一节解决次要矛盾&#xff08;矩形色带&#xff09;。 我们的想法如图&#xff1a; 1&#xff0c;我们找到稳定黄色的最左边&#xff0c;最右边两点&…

第24周:文献阅读

目录 摘要 Abstract 一、现有问题 二、提出方法 三、创新点 模型结构创新 强化学习与GAN结合 属性特征与通顺性优化 四、方法论 生成对抗网络&#xff08;GAN&#xff09; 强化学习&#xff08;RL&#xff09; 模型组件 五、实验研究 数据集 数据预处理 评价指…

SQL server学习05-查询数据表中的数据(上)

目录 一&#xff0c;基本格式 1&#xff0c;简单的SQL查询语句 2&#xff0c;关键字TOP 3&#xff0c;关键字DISTINCT 二&#xff0c;模糊查询 1&#xff0c;通配符 三&#xff0c;对结果集排序 1&#xff0c;不含关键字DISTINCT 2&#xff0c;含关键字DISTINCT 3&…

【Azure 架构师学习笔记】- Azure Function (1) --环境搭建和背景介绍

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Function 】系列。 前言 随着无服务计算的兴起和大数据环境中的数据集成需求&#xff0c; 需要使用某些轻量级的服务&#xff0c;来实现一些简单操作。因此Azure Function就成了微软云上的一个必不可少的组成部分。 …