我应该在模板函数中按const引用还是按值传递?

max*_*max 1 c++

当选择按const引用传递还是按值传递时,对于大型类来说,选择似乎很清楚:必须使用constref避免昂贵的副本,因为仅在有限的情况下(如果副本构造函数没有副作用,或者如果副本来自一个右值)。

对于较小的类,按值传递似乎更具吸引力:

  • 就像取消引用一样快
  • 避免混叠(易错且对性能不利,因为它会迫使编译器更加保守)
  • 如果仍然需要复制,它将使复制在编译器可以使用复制省略的范围内

那么编写模板函数时的最佳实践是什么,而参数的类是大还是小并不总是很明显?

Vit*_*meo 5

(假设您只想从传递的值中读取数据。)

我认为最好的选择是路过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块和“元类” 时,使用代码生成会有更好的解决方案。