内联函数与constexpr函数有什么区别?

46
使用新的C++11标准时,什么情况下应该使用inline关键字而不是constexpr关键字? constexpr关键字是否比inline提供任何额外的优化,还是仅仅声明必须在编译时计算这些内容?
为什么constexpr在某些情况下可以在GCC上工作,即使调用不是常量,例如对非constexpr变量调用foo(x)? 这是GCC中的错误还是实际上是标准的一部分?
3个回答

53

断言某些东西可以在编译时计算出来,这是一种非常强的优化。

内联只是通过将函数体复制/粘贴到调用位置中来删除函数调用。函数体仍然需要执行,您只是节省了函数调用的开销。

但是,如果您使相同的代码在编译时进行评估,则在运行时是免费的。

但是,无论是inline还是constexpr都不是主要关于优化的。 inline的主要目的是抑制单一定义规则,以便可以在头文件中定义函数(这对于模板很有用,并且顺便也使内联优化更加容易)

constexpr之所以存在是因为它在元编程中很有用,顺带地,它可以通过将更多的计算移动到编译时来帮助编译器更好地优化代码。


6
根据此问题所述,当constexpr函数未在需要常量表达式的上下文中使用时,编译器没有义务在编译时计算表达式。 - ildjarn
但它仍然指定计算可以在编译时执行。因此,与“inline”一样,它并不真正涉及优化,但它可能提供额外的信息,编译器可以用来进行优化。 - jalf
2
@jalf 我不同意inline不是主要用于优化的说法。如果我没记错,在头文件中定义的函数默认是内联的,尽管像一直以来的情况一样,这只是一个编译器提示。只要你有适当的头文件保护措施,那么ODR就不是真正相关的问题了。但我愿意在这里接受教育 :-) - boycy
2
优化的作用在于,如果你将某个东西标记为“inline”,那么你可以使其定义在每个使用它的翻译单元中可见,而不会导致ODR冲突,这使得编译器更容易执行内联优化。因此,该关键字本身并不能启用任何新的优化,但使用它可以改变代码结构,这些改变可以使编译器进行优化(尽管现代编译器可以跨翻译单元进行优化,然后像这样的技巧也不再必要)。 - jalf
1
你提到inline对于模板很有用,但实际上并不是这样的。函数模板和类内定义的成员函数会自动被视为具有inline关键字。它对于在多个翻译单元中定义的自由函数(通常由于在头文件中定义并包含在不同的TU中)非常有用,并通过告诉链接器在链接时丢弃所有但一个定义来避免链接器错误。 - SirGuy
显示剩余4条评论

5

引用维基百科:

C++0x将引入关键字constexpr,允许用户保证函数或对象构造函数是编译时常量。

如果函数非常短,则将函数标记为inline。 如果需要在编译时必须得到结果(模板参数或数组大小),则将函数标记为constexpr。 我相信一个函数如果需要的话可以同时具备这两个标记。

常量表达式函数或构造函数可以使用非constexpr参数进行调用。 就像可以将constexpr整数文字分配给非constexpr变量一样,constexpr函数也可以使用非constexpr参数进行调用,并将结果存储在非constexpr变量中。 当表达式的所有成员都是constexpr时,该关键字仅允许编译时常数的可能性。

因此,GCC没有错误。


-3

虽然inline告诉编译器“这个函数在这个翻译单元中被使用,不对其他目标文件公开”,但编译器很可能会将函数体插入调用者中。constexpr函数告诉编译器“这个函数没有副作用,也不依赖于除参数本身之外的前提条件。”

constexpr变量只是说“这个变量不会改变,它的数据可以包含在代码中”。但是,如果您在函数静态或非静态定义一个constexpr变量,则会有所区别。例如,如果constexpr数组是非静态的,则gcc仅使用硬编码的mov指令将数据移动到堆栈上,而static constexpr则将数据存储在.text部分中。

没有捕获的Lambda表达式分配给变量可以是constexpr,而有捕获的则不行,因为没有捕获时它们不需要保存捕获的内存,并且它们的工作方式类似于重载了operator()的空类(但它们甚至可以通过简单的一元加号转换为普通函数指针:+[]{})。


“inline”并不意味着内部链接,就像你在第一段所说的那样。 - M.M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接