模板化的数学函数应该采用值还是 const 引用?
Posted
技术标签:
【中文标题】模板化的数学函数应该采用值还是 const 引用?【英文标题】:Should templated math functions take values or const references? 【发布时间】:2016-10-12 21:24:34 【问题描述】:假设我想实现一些简单的数学函数;例如,假设它是 (C++17's) std::clamp
的重新实现:此函数接受一个数字、一个下限和一个上限,如果它超出了它们定义的范围,则将该数字设置为这些边界之一。如果是具体的数字类型,比如int
,我会写:
constexpr int clamp(int x, int lower_bound, int upper_bound)
return x < lower_bound ? lower_bound : ( upper_bound < x ? upper_bound : x );
但如果它是一个模板,我看到sample implementation 这可能是标准将使用的const&
而不是值。因此,让引用变得更简单,例如:
template <typename T>
constexpr T clip(const T& x, const T& lower_bound, const T& upper_bound)
return x < lower_bound ? lower_bound : ( upper_bound < x ? upper_bound : x );
我的问题是:
对于T
的简单数字类型,采用 const 引用有什么好处?
同上,对于将单个数字包装为数据成员的一些抽象事物的类型(例如 std::chrono
持续时间)?
为什么在任何相对简单、(constexpr?)、无副作用的数学函数的一般情况下,取一个const&
是一个更好的主意(而且完全是这样)?
注意事项:
我意识到,当您拥有某种 k 维向量类型或boost::rational
s 和其他类似数字的类型时,使用 const&
可能会开始有意义;但即便如此,编译器不会优化复制吗?
我不是询问任何任意 C++ 函数以及它是否应该只按值获取其参数,这显然是个坏主意。
【问题讨论】:
它们不能是简单的数字类型。它们可能是表现得像数字的类型。毕竟你可能不想复制它们。此外,一个 const 引用绑定到一个临时的,有时它很有用。 @Zereges:再说一遍,为什么?编译器不会优化差异吗? @einpoklum 它当然可以与您给定函数的任何其他类型一样 @Zereges:我猜的不是堆上有数据的类型。 【参考方案1】:
对于 T
的简单数字类型,采用 const 引用有什么好处?
没有。
同上,对于将单个数字包装为数据成员的一些抽象事物的类型(例如 std::chrono
持续时间)?
没有。
为什么在任何相对简单的(constexpr?)无副作用的数学函数的一般情况下,采用const&
是一个更好的主意(而且完全是这样)?
想象一个使用动态分配的 bigint 类型;复制这样的类型很昂贵。
编译器不会优化复制吗?
只有在能够证明复制值没有副作用的情况下,这很难做到,除非所涉及的所有代码对编译器都是可见的。 (因此,如果您的 bigint 使用 GMP,那么您就不走运了。)
【讨论】:
【参考方案2】:对于
T
的简单数字类型,采用 const 引用有什么好处?
不,我不这么认为,但也没有处罚。
同上,对于将单个数字包装为数据成员的一些抽象事物的类型(例如
std::chrono duration
)?
同上。
为什么在一般情况下取一个
const&
然后取一个值是更好的主意?
标准库算法不仅为基本类型设计,还为用户定义类型设计,复制起来可能并不便宜。对于这些类型,使用 const&
可以避免复制的惩罚,同时不会损害基本类型的使用。
【讨论】:
但是,通过微不足道的编译器优化真的有惩罚吗?请参阅我的第一个注释。 我在你写评论的时候更新了答案。以上是关于模板化的数学函数应该采用值还是 const 引用?的主要内容,如果未能解决你的问题,请参考以下文章
C++,在函数中采用 const lvalue 和 rvalue 引用