菜单
首页/博客/constexpr 和 inline

constexpr 和 inline

inline

inline作用有二:

  1. 链接可重复(首要):允许函数定义多次,但会合并为一个,避免重定义
  2. 内联展开提示(次要):建议编译器内联展开,也就是直接把函数嵌入到调用处

inline使用场景:

  1. 可重复实现:实现在h,hpp的函数,如果类方法,或直接写进去的函数

    (实现在hpp的类里的函数,默认带inline)

    /// @file foo.hpp
    #pragma once
    
    class Foo{
        int x;
        
        // 这个构造函数默认inline
        Foo(int x){
            this->x = x;
        }
        
        // 类里实现的函数,默认 inline,可省
        inline int Bar(int y){
            return x * y;
        }
    }
    
    // 全局函数,默认没有 inline,不加 inline 会报错
    inline int Bar(int x, int y){
        return x * y;
    }
    
    // 若是模板,编译器会自己 inline,可省
    template<typename T>
    T Qux(T a, T b){
        return a * b;
    }
    
  2. 内联函数用,但inline只是建议编译器,实际未必展开,如果开优化-O了不inline也可能展开(所以只是建议,选择权在编译器)

    /// @file math-tool.hpp
    #pragma once
    
    namespace math_tool{
        
    // 这里用了可能展开,不过实现在hpp里了也不得不inline了(作用1)
    inline float Sin(float x){
        // 调用了某种数学硬件优化库
        return arm_sin_f32(x);
    }
        
    // inline 时,声明和实现应在同一头文件。若仅 .hpp 声明
    // 但实现放在 .cpp,不触发内联展开优化
    // 如下:
    inline float Cos(float x);
    }
    

    如果展开,就不会跳到Sin再跳到arm_sin_f32,相当于直接把 arm_sin_f32 放在调用处。

    像这样调用函数时,栈的跳转可能比函数运算的开销还大(调用函数要压栈、换指针,也是需要时间的),尤其是这样频繁调用的函数,cpu在函数和现场跳来跳去,得不偿失,就需要优化了。

    内联展开,相当于 copy 了一份函数,放在调用处,内存可能变大了,这算是优化的空间换时间(?)

constexpr

inline 强调函数的链接,和内联展开优化。而 constexpr 是用于编译器优化:这个变量/函数需要在编译器就算出来。

/// @file test.cpp
// 以下都是全局作用域

constexpr int Foo(int a){
    return a * 10;
}

constexpr int a = Foo(4);

// 也是对的,相当于 array[100]
int array[Foo(10)];

const 和 constexpr 变量的区别:

constexpr 函数自带 inline:

两者关系

其实现在编译器会自动优化,比如 1 + 2 会自动编译期算出 3,而不是放到编译完的程序里,每次运行算一遍。

这样说,inline 如果展开了,编译器再优化了,其实就是 constexpr。但优化不是 inline 做的,inline 更重要的任务是防止重定义。

constexpr 让程序限制死了在编译器能用,指定了要优化这里,所以是编译器优化指令。

inline就算展开优化为了常数,也不能在编译器用。