开发者

Why do we not pass POD by reference in functions?

I've always been told that we should not pass POD by reference. But recently I've discovered that开发者_开发问答 a reference actually takes no memory at all.

So why do we choose to write:

void DoSomething(int iNumber);

instead of:

void DoSomething(const int& riNumber);

is it not more efficient?


Actually in this case (using int) passing by value is probably more efficient, since only 1 memory-read is needed instead of 2, to access the passed value.

Example (optimized using -O2):

int gl = 0;

void f1(int i)
{
    gl = i + 1;
}

void f2(const int& r)
{
    gl = r + 1;
}

int main()
{
    f1(1);

    f2(1);
}

Asm

    .file   "main.cpp"
    .text
    .p2align 2,,3
.globl __Z2f1i
    .def    __Z2f1i;    .scl    2;  .type   32; .endef
__Z2f1i:
LFB0:
    pushl   %ebp
LCFI0:
    movl    %esp, %ebp
LCFI1:
    movl    8(%ebp), %eax
    incl    %eax
    movl    %eax, _gl
    leave
    ret
LFE0:
    .p2align 2,,3
.globl __Z2f2RKi
    .def    __Z2f2RKi;  .scl    2;  .type   32; .endef
__Z2f2RKi:
LFB1:
    pushl   %ebp
LCFI2:
    movl    %esp, %ebp
LCFI3:
    movl    8(%ebp), %eax
    movl    (%eax), %eax
    incl    %eax
    movl    %eax, _gl
    leave
    ret
LFE1:
    .def    ___main;    .scl    2;  .type   32; .endef
    .p2align 2,,3
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB2:
    pushl   %ebp
LCFI4:
    movl    %esp, %ebp
LCFI5:
    andl    $-16, %esp
LCFI6:
    call    ___main
    movl    $2, _gl
    xorl    %eax, %eax
    leave
    ret
LFE2:
.globl _gl
    .bss
    .align 4
_gl:
    .space 4


Not passing PODs by reference seems like a too general rule. PODs can be huge, and passing references to it would be worth. In your particular case, an int is the same size than the pointer that most -if not all- implementations use in the background to actually implement references. There is no size difference between passing an int or a reference, but now you have the penalty of an extra level of indirection.


Passing by reference is passing by pointer in disguise.

With small data, it can be faster to access it as values rather than having to deference the pointer several times.


Because it is a meaningless efficiency gain 99.999% of time, and it changes the semantics. It also prevents you from passing in a constant value. For example:

void Foo(int &i) { }
Foo(1);  // ERROR!

This would work however:

void Foo(const int &i) { }
Foo(1);

Also, it means that the function can modify the value of i such that it is visible to the caller, which may well be a bad thing (but again, you could certainly take a const reference). It comes down to optimizing the parts of your program where it matters and making the semantics of the rest of the code as correct as possible.

a reference actually takes no memory at all.

Not sure who told you that, but it's not true.


Depends what you mean by efficiency.

The main reason we pass objects by constant reference is because doing so by value will invoke the object's copy constructor, and that can be an expensive operation.

Copying an int is trivial.


There is no right answer, or even obvious general preference. Passing by value can be faster in some instances, and very importantly, can eliminate side effects. In other cases, passing by reference can be faster, and allow information to be more readily returned from a given function. This holds for POD data types. Keep in mind, a struct can be a POD, so size considerations vary between PODs even.


The real answer is habit.
We have an ingrained culture of attempting macro optimizations on our code (even if it rarely makes any real difference). I would be surprised if you can show me code were passing by value/reference (an integer) makes any difference.

Once you start hiding the type via template we start using const reference again (because it may be expensive to copy some types).

Now if you had asked about generic POD then there could be a cost difference as POD can get quite large.

There is small advantage in the first version that we do not need an extra identifier if we are mutating the original value.

void DoSomething(int iNumber)
{
    for(;iNumber > 0; --iNumber)   // mutating iNumber
    {
       // Stuff
    }
}

// No real cost difference in code.
// Just in the amount of text we need to read to understand the code
//
void DoSomething(int const& iNumber)
{
    for(int loop = iNumber;loop > 0; --loop)   // mutating new identifier
    {
       // Stuff
    }
}


No it is not. The example with int is simply the same. It matters when you have "heavier" objects.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜