C++中constexpr与函数参数转发的操作方法

 更新时间:2024年02月27日 10:23:01   作者:leapmotion  
constexpr是c++11引入的关键字,c++11的constexpr的函数中只是支持单句代码,c++14限制放宽,可以在里边写循环及逻辑判断等语句,本文探讨关于constexpr的函数中参数的现象,以及如果参数是constexpr如何做转发,感兴趣的朋友一起看看吧

本文和大家探讨下关于constexpr的函数中参数的现象,以及如果参数是constexpr如何做转发

关于constexpr函数

constexpr是c++11引入的关键字,c++11的constexpr的函数中只是支持单句代码,c++14限制放宽,可以在里边写循环及逻辑判断等语句。
constexpr形容函数表示该函数被编译期计算或者调用。但是只能是可能被编译期计算,举例来说:

constexpr int square(int num) {
    return num * num;
}

假设这里有一个计算平方的函数是constexpr形容,那么我如果这样调用:

int main() {
    int val = square(12);
}

我们可以看下编译后的汇编:

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 144 // 这里
        mov     eax, 0
        pop     rbp
        ret

可以看到main函数里在运行时根本没有去调用square函数,而是在编译期直接将square函数计算得出144并赋值。也就是说传递参数是字面量(或者constexpr变量)可以被认定是编译期调用。

那么我们如果换一种方式调用呢?

int main() {
    int p = 12;
    int val = square(p);
}

通过参数传递进来,再来看下汇编:

square(int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, eax
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 12
        mov     eax, DWORD PTR [rbp-4]
        mov     edi, eax
        call    square(int)
        mov     DWORD PTR [rbp-8], eax
        mov     eax, 0
        leave
        ret

略显复杂,不需要关注很多汇编代码,我们只需要知道运行时确实会去执行square函数。

constexpr函数小结

那也就是说constexpr函数并不是完全是编译期执行,编译器其实和你传递的参数或者函数内部的一些逻辑来判断。

constexpr参数传递

这里假设有一个这样的例子:

// ...
struct AddSum {
    void add(int val) {
        constexpr auto sq = square(val);
        sum += sq;
    }
    int sum = 0;
};
int main() {
    constexpr int p = 12;
    AddSum a;
    a.add(p);
    return 0;    
}

需要把一些编译期的数字的平方加起来,在add函数中使用constexpr来接收square的返回值,因为这样可以使得square是编译期执行的,不过可惜的是这里不能通过编译,因为square不能确定val是不是constexpr。但是我们明明传递的就是constexpr的变量。那么问题来了,如何在传参数的时候能够将constexpr的属性进行传递呢?

C++并不支持在形参使用constexpr限制:

// error: Function parameter cannot be constexpr
void add(constexpr int val) { 
    // ...
}

且使用完美转发也不能将constexpr进行传递。

template<typename T>
void add(T&& val) {
     constexpr auto sq = square(val); // error
}

使用非类型模板参数

既然用到了模板,那么我们完全可以将val作为非类型的模板参数进行传递:

template<int val>
void add() {
    constexpr auto sq = square(val);
    sum += sq;
}

如果这里就结束的话,那确实不值得讨论了,非类型模板参数的问题是只能传递一些基本类型,我们自定的类constexpr对象则不能传递。

值内嵌的类型

其实我们可以借助一个helper类,将要传递的值封装在这里类里,然后需要的时候取出来。如下:

struct Helper {
    static constexpr int value() {
        return 12;
    }
};
struct AddSum {
    template<typename H>
    void add(H) {
        constexpr auto sq = square(H::value());
        sum += sq;
    }
    int sum = 0;
};
int main() {
    AddSum a;
    a.add(Helper{});
    return 0;
}

可以看到我们Helper中value用来封装了要传递的int值,然后将Helper的对象传递给add,进一步可以获取到value。

这里不能通用,因为我们需要在helper里写死这个值,又没办法将值传递给helper,感觉又绕回来了。哈哈,这里我们使用宏来定义一个通用的模式:

#define CONSTEXPR_HELPER(...)                 \
struct Helper {                               \
    static constexpr decltype(auto) value() { \
        return __VA_ARGS__;                   \
    }                                         \
};
int main() {
    AddSum a;
    using H = CONSTEXPR_HELPER(12);
    a.add(H{});
    return 0;
}

这种情况下就可以使用任意类型的的constexpr属性进行传递了。同时也可以将值在调用时传递而不是写死。

constexpr的lambda表达式

C++17出现了constexpr形式的lambda表达式,我们可以借助它来实现constexpr的传递

struct AddSum {
    template<typename H>
    void add(H h) {
        constexpr auto sq = square(h());
        sum += sq;
    }
    int sum = 0;
};
int main() {
    AddSum a;
    a.add([](){
        return 12;
    });
    return 0;
}

这里我们使用constexpr的lambda对象传递给add函数,同时传递过来的lambda表达式仍然具有lambda属性,然后调用将值获取到。

这样也就是在C++17版本不需要使用宏来定义内嵌值的类型。

总结

本篇文章就是和大家探讨下constexpr的参数传递的问题,可以使用非类型模板参数,宏定义,lambda表达式做到。

感谢大家,点个赞吧~

ref

https://mpark.github.io/programming/2017/05/26/constexpr-function-parameters/

到此这篇关于C++中constexpr与函数参数转发的文章就介绍到这了,更多相关constexpr函数参数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言实现将double/float 转为字符串(带自定义精度)

    C语言实现将double/float 转为字符串(带自定义精度)

    这篇文章主要介绍了C语言实现将double/float 转为字符串(带自定义精度),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 排序算法模板实现示例分享

    排序算法模板实现示例分享

    这篇文章主要介绍了排序算法模板实现示例,需要的朋友可以参考下
    2014-03-03
  • OpenCV实现马赛克和毛玻璃滤镜效果

    OpenCV实现马赛克和毛玻璃滤镜效果

    这篇文章主要为大家详细介绍了OpenCV实现马赛克和毛玻璃滤镜效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • C语言详解函数与指针的使用

    C语言详解函数与指针的使用

    C语言这门课程在计算机的基础教学中一直占有比较重要的地位,然而要想突破C语言的学习,对函数和指针的掌握是非常重要的,本文将具体针对函数和指针的关系做详尽的介绍
    2022-04-04
  • Win11+ VS2022编译 FFmpeg6.0 静态库的详细过程

    Win11+ VS2022编译 FFmpeg6.0 静态库的详细过程

    这篇文章主要介绍了Win11+VS2022编译FFmpeg6.0静态库的方法,本文通过图文实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • 剑指offer之C语言不修改数组找出重复的数字

    剑指offer之C语言不修改数组找出重复的数字

    今天小编就为大家分享一篇关于剑指offer之C语言不修改数组找出重复的数字,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • 详解C++编程中的主表达式与后缀表达式编写基础

    详解C++编程中的主表达式与后缀表达式编写基础

    这篇文章主要介绍了C++编程中的主表达式与后缀表达式编写基础,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01
  • C++ opencv利用grabCut算法实现抠图示例

    C++ opencv利用grabCut算法实现抠图示例

    这篇文章主要为大家介绍了C++ opencv利用grabCut算法实现抠图的代码示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 详细理解函C语言的函数栈帧

    详细理解函C语言的函数栈帧

    这篇文章主要为大家介绍了C语言的函数栈帧,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助,希望能够给你带来帮助
    2021-11-11
  • C语言 递归实现排雷游戏

    C语言 递归实现排雷游戏

    扫雷是电脑上很经典很经典的传统老游戏,从小编第一次摸到计算机开始就玩过扫雷,虽然当时并不理解玩法原理,但终是第一次玩电脑游戏,下面来从扫雷的前世今生讲起
    2021-11-11

最新评论