现代C ++中最有效的参数获取方式?

时间:2019-07-03 11:27:26

标签: c++ reference

可以采用许多不同的方式获取参数:

void foo(T obj);
void foo(const T obj);
void foo(T& obj);
void foo(const T& obj);
void foo(T&& obj);
void foo(const T&& obj);

我不包括通过指针获取参数,因为指针可能已经是T类型。

前两种方式是按值获取对象,这将导致对象被复制(如果没有通过智能编译器进行优化)。

其他方法通过引用来获取对象(前两个通过l值引用,后两个作为r值引用),该对象应省略复制。合格的const版本保证不会修改引用的对象。

如果我希望函数能够将各种值(变量,从函数返回的临时变量,文字等)作为参数,那么非对象修改版本(const的原型应该是什么? )和可能修改对象的版本(不是const)版本,如果我希望它是最有效的版本(避免调用复制构造函数,移动构造函数,赋值运算符,创建临时对象等)?

我不确定应该使用哪种类型的引用。如果这是一个主观的问题,我正在寻找每种方法的利弊。

2 个答案:

答案 0 :(得分:6)

存在所有这些用于参数声明的组合的原因。这取决于您的需求:

  • void foo(T obj);
    • 呼叫者不希望obj被修改(因此已被复制)
    • foo 可以修改 obj [是副本]
    • 如果foo未修改obj,则相对于const T&,这是 still 首选-假设 T很小(适合1-2个CPU寄存器)

  • void foo(const T obj);
    • 呼叫者不希望obj被修改(因此已被复制)
    • foo 无法修改 obj
    • 请记住,const通常可以帮助您发现错误。这就是为什么通常将其用于避免意外修改obj的原因。 (例如if (obj = 5)

  • void foo(T& obj);
    • 呼叫者希望更改为obj
    • foo 可以修改 obj
    • 引用表示不进行复制。因此,这对于传递昂贵的复制对象很有用。但是,对于小型类型(intdouble等)来说,这可能会很慢,这意味着传递副本会更好。

  • void foo(const T& obj);
    • 呼叫者不希望修改obj
    • foo 无法修改 obj
    • 引用表示不进行复制。由于它是不可修改的,因此这是参数化大型只读容器的理想方法。因此:对于昂贵的复制对象来说非常便宜,对于小型类型来说可能会很慢。

  • void foo(T&& obj);
    • 呼叫者不在乎obj会发生什么,并且如果以后没空,也没问题
    • foo 可以通过将信息移到另一个地方来窃取数据来进行修改 obj

  • void foo(const T&& obj);
    • foo 无法修改 obj,这很少有用
    • 但是,这不允许左值作为参数传递。

有很多特殊情况,所以这不是完整列表。


一些额外的位:

  • copy-swap-idiom
  • (const T& obj)通常比仅仅(T obj)更糟糕,原因有很多。但是请记住,呼叫者可以始终将T设为std::reference_wrapper<const T>以避免复制。不过,这可能会破坏功能。
  • 即使您没有std::move,也要进行很多操作-假设该类型具有必要的运算符。
  • 函数/ Lambdas?按值传递:template <typename F> void execute(F f) { f(); }

最后,值得分享的是Bisqwitthis video制作的流程图,可以制作出精美的图形:

parameter declaration flow chart

答案 1 :(得分:1)

  

在现代C ++中获取参数的最有效方法?

最有效的方式取决于参数的类型以及是否将修改参数。

对于适合处理器寄存器的类型,通过副本传递是最有效的。 (编译器可以选择将变量传递到寄存器中而不是堆栈中。)

对于较大的类型,包括参数将被修改的情况,通过引用传递是有效的(并且安全的)。通过指针传递是有效的,但不如通过引用传递安全。

对于许多参数,请考虑将其放入结构并将结构传递给函数(通过引用或const引用)。

使用适合寄存器内部的数据类型时,处理器效率最高。

注意:这是一个微优化,通常保留给时间紧迫的应用程序使用。在担心传递参数的效率之前,最好先进行鲁棒性和正确性研究。