4

根据我从编译器作者那里收集到的信息,就效率而言,值类型比引用/指针更受欢迎。

这是因为当您不必关心别名、外部更改的内存(指针所指的)、指针取消引用的成本等事情时,值类型更容易推理。我不得不说,虽然我理解这些担忧,但我仍然对具体案例有一些疑问。

案例#0

void foo(const float& f) 好的,我们在这里有一个参考,但它是不变的!当然,我们对它有一个恒定的视图(参考),所以在外部它可能会发生变化,但它只能发生在多线程世界中,如果没有使用同步原语,我不确定编译器是否必须考虑它。显然,如果在内部我们使用另一个指针/引用任何浮点变量,我们可能会面临修改f参数的风险。编译器可以将此参数视为安全的(假设我们不使用任何 ref/ptr 在内部浮动)吗?

情况1

void foo(vector<int> f) 从 C++11/14 程序员的角度来看,我知道vector可以安全地将其移入函数中。众所周知,容器内部保存着一个指向数组的指针。编译器是否会将指针视为安全的(没有外部修改),因为我们刚刚获得了向量的副本,所以我们是它的唯一所有者?

换句话说:被复制的对象是否被视为安全对象(因为逻辑上我们会克隆该对象),或者编译器不允许做出这样的假设,并且任何ptr/ref 成员都必须被视为具有潜在危险的复制 ctor/op 可能没有正确复制。复制共享资源时,程序员不应该负责处理它们吗?

底线

Do 常量指针/引用和复制的复杂对象通常比原语的副本慢,因此在性能关键代码中应尽可能避免;还是它们的效率稍低一些,我们不应该为此烦恼

4

3 回答 3

3

作为现代 C++ 的一般规则:

  1. 对于(廉价复制)原始类型,如int, floatordouble等​​,如果它是输入只读)参数,只需按值传递

    inline double Square(double x)
    {
        return x*x;
    }
    
  2. 相反,如果输入参数的类型复制不便宜,但移动便宜,例如std::vector,std::string等,请考虑两种子情况:

    一个。如果函数只是观察值,则通过const引用传递(这将防止无用的潜在昂贵的深层副本):

    double Average(const std::vector<double> & v)
    {
        .... // compute average of elements in v (which is read-only)
    }
    

    湾。如果函数正在获取参数的本地副本则按值传递,并std::move从值传递(这将允许优化,这要归功于移动语义):

    void Person::SetName(std::string name)
    {
        m_name = std::move(name);
    }
    
于 2013-08-01T08:39:07.963 回答
2

(开始作为评论,但它不适合。)

案例#0 已经被讨论到死了,例如:

通过引用传递原始类型会适得其反吗?

这已经是其他两个问题的重复。特别是,我发现这个答案也是您案例#0 的一个很好的答案。相关问题:

案例 #1 我不清楚:您需要一份副本还是要搬家?两者之间存在巨大差异,从你写的内容中不清楚你需要哪一个。

如果一个参考就足够了,但你做了一个副本,那么你就是在浪费资源。

如果您需要进行深层复制,那么这就是它的全部内容,无论是引用还是移动都无济于事。

请阅读此答案并修改案例#1。

于 2013-08-01T08:54:36.357 回答
1

案例#0

- 可以从外部修改:

void foo(const float& f) {
 ..use f..
 ..call a function which is not pure..
 ..use (and reload) f..
}

案例 #1 ... 编译器是否会将指针视为安全的(没有外部修改),因为我们刚刚获得了向量的副本,所以我们是它的唯一所有者?

——它一定是悲观的。可以教它依赖一个实现,但一般来说,它没有合理的方法来跟踪该指针,通过所有可能的构建场景进行任意构建,以验证它是安全的,即使实现是可见的。

底线:

分配和复制容器的成本往往比加载和存储的成本高得多——取决于您的程序、硬件和实现!

通过引用传递小对象和内置函数并不意味着优化器必须在实现可见时将其视为引用。例如,如果它看到调用者正在传递一个常量,它可以自由地根据已知的常量值进行优化。相反,创建副本会干扰优化程序的能力,因为复杂性会增加。为是否通过值传递这种琐碎/小类型而烦恼是一种旧的微优化。相比之下,复制(非 SSO)字符串或向量 OTOH 可能是巨大的。首先关注语义。

我编写了大量的性能关键代码,并通过(适当的 const 限定)引用传递几乎所有内容——包括内置函数。您当时正在计算指令和内存速度(对于您的参数),这在台式机和便携式计算机中非常低。在确定之前,我在台式机和笔记本电脑上进行了大量测试。我这样做是为了统一 - 您无需担心在嵌入式之外引入引用(存在开销)的成本。同样,制作不必要的副本和任何必要的动态分配的成本往往要高得多。还要考虑到对象有额外的构造、复制和销毁功能要执行——即使是看起来很无辜的类型,复制的成本也比引用的成本高得多。

于 2013-08-01T09:21:29.063 回答