当选择按const引用传递还是按值传递时,对于大型类来说,选择似乎很清楚:必须使用constref避免昂贵的副本,因为仅在有限的情况下(如果副本构造函数没有副作用,或者如果副本来自一个右值)。
对于较小的类,按值传递似乎更具吸引力:
那么编写模板函数时的最佳实践是什么,而参数的类是大还是小并不总是很明显?
(假设您只想从传递的值中读取数据。)
我认为最好的选择是路过const&,例如:
某些对象无法复制,复制成本高昂或在其复制构造函数中包含副作用。
尽管采用原语const& 可能会导致轻微的性能下降,但与上面项目符号中描述的问题相比,这是一个较小的损失。
理想情况下,您想做这样的事情(在这里,我不太注意在复制构造函数中有副作用的小类):
template <typename T>
using readonly = std::conditional_t<
sizeof(T) <= sizeof(void*),
T,
const T&
>;
template <typename T>
void foo(readonly<T> x);
Run Code Online (Sandbox Code Playgroud)
问题是,T不能从对to的调用中推论得出foo,就像在“不可推论的上下文”中那样。
这意味着您的用户将必须调用foo<int>(0)而不是foo(0),因为T=int无法从编译器中推断出。
(我想重申一下我在上面使用的条件是非常幼稚的,并且有潜在的危险。您可能想简单地检查一下T是不是原始的还是POD比它小的void*。)
您可以做的另一件事是std::enable_if_t控制要调用的函数:
template <typename T>
auto foo(T x) -> std::enable_if_t<(sizeof(T) <= sizeof(void*))>;
template <typename T>
auto foo(const T& x) -> std::enable_if_t<(sizeof(T) > sizeof(void*))>;
Run Code Online (Sandbox Code Playgroud)
显然,这需要大量额外的样板...也许当(如果?)我们将获得constexpr块和“元类” 时,使用代码生成会有更好的解决方案。