小而简单的结构体应该通过const引用传递吗?

22

我一直被教导说,尽可能使用const引用而不是值来传递非原始类型,例如:

void foo(std::string str);//bad
void foo(const std::string &str);//good

但我今天在想,也许一些简单的用户定义类型更适合通过值传递,例如:

class Vector2
{
public:
    float x, y;
    ...constructors, operators overloads, utility methods, etc...
};

void foo(Vector2 pos);
void foo(const Vector2 &pos);//is this really better than by value?
void foo(float x, float y);//after all isn't by value effectively doing this?

我认为通过引用传递Vector2对象要比值传递更加消耗资源,因为编译器现在使用指针和解引用来访问const Vector2 &pos版本?

这是真的吗?简单的对象最好通过值传递吗?应该在哪里划线?


请注意,如果您的类类型具有用户声明的构造函数,则它不是POD。 - James McNellis
11
经验法则是:使用 const 引用传递,除非 T 是基本类型或者 sizeof(T) <= sizeof(void*) - GManNickG
2
@GMan,你比我快了(加1)。虽然我会概括为它可能小于或等于处理器的字长。而且,应该检查处理器规格以找到最佳大小。如果我没记错的话,在某些情况下编译器可能会将const ref优化为按值传递,而不考虑大小。我想在C++0x中这尤其正确,因为有了新的移动语义。 - Nathan Ernst
@James,我猜现在不是按照C++03标准,尽管从事情的样子来看,C++0x将会把它视为POD。 - Fire Lancer
我认为很少会出现对象小于处理器字长和/或sizeof(void*)的情况... @Fire,你真的需要画一条非常细的线;-) - KedarX
6个回答

9

是的,简单对象应该按值传递。根据架构来划分线条。如果有疑问,请进行性能分析。


3

出于政策考虑,我会将任何不是基本C数据类型的对象传递为const引用。这样做更容易记忆。


2

通过const引用传递可以避免构造新对象。如果你的构造函数是非平凡的,例如它分配内存,那么通过const引用传递比通过值传递要好得多。

在C#世界中,你可以通过选择将某些东西作为类或结构体来做出这个决定。类使用引用语义,而结构体使用值语义。我所读过的所有内容都表明,除非它非常小(即大约16字节),否则通常应该选择将所有东西都作为类。如果你正在寻找在C++中何时使用值传递与const引用传递的分界点,16字节似乎是一个合理的阈值。


1
POD类型没有非平凡的复制构造函数(或者确实没有非平凡的默认构造函数或析构函数)。 - Ben Voigt
2
OP 没有明确声明 POD 类型。他说“简单”的用户定义类型,然后给出了一个示例,表明它具有构造函数。 - Matt Davis

2

我没有看到提到的一个因素是该函数将如何处理传入的值。除非该程序在内联中进行扩展,否则操作通过引用传递的数据将需要比操作通过值传递的数据更多的代码。如果传入结构体的字段平均每个字段访问一次以下,那么这种额外开销将与复制结构体的开销相比小得多。如果它们平均每个字段被访问多次,最好将它们放在堆栈上。如果接口已经设置为传递引用的结构体受到了重视,那么调用的例程复制感兴趣的所有部分可能是有意义的。另一方面,如果被调用的例程将复制大量或全部结构,那么最好传递的方式就是按值传递。


1

一篇旧但是清晰的关于传递参数的分析可以在 http://www.ddj.com/184403855 找到。当然,c++0x通过移动语义消除了许多这样的问题,但该论文提供了许多理由来说明为什么移动语义是可取的。


0
我的想法是,通过引用传递Vector2比按值传递实际上更昂贵,因为编译器现在使用一个指针和解除引用来访问const Vector2 &pos版本。
如果传递的对象大小小于指向对象的指针的大小,那么这是真的。例如,char const&可能比char更昂贵。

3
如果重复解引用会导致比不传递大量数据节省的开销更大,那么这也可能是正确的。或者,如果引用由于别名问题而防止了优化。或者,如果引用损害了局部性。 - Dennis Zickefoose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接