23

Which of the following examples is the better way of declaring the following function and why?

void myFunction (const int &myArgument);

or

void myFunction (int myArgument);
Don
  • 3,987
  • 15
  • 32
Etan
  • 17,014
  • 17
  • 89
  • 148
  • 1
    See also [these rules of thumb](http://stackoverflow.com/a/2139254/140719) for passing arguments to functions. – sbi Jan 18 '12 at 14:57

14 Answers14

39

Use const T & arg if sizeof(T)>sizeof(void*) and use T arg if sizeof(T) <= sizeof(void*)

Alexey Malistov
  • 26,407
  • 13
  • 68
  • 88
  • 13
    This is misleading IMO. I wouldn't choose `const double &t` over `double t` but your statement suggests doing that on 32 bit systems. – Mehrdad Afshari Oct 14 '09 at 15:58
  • 6
    Yes, perhaps, double is an exception. – Alexey Malistov Oct 14 '09 at 16:02
  • 1
    There are more exceptions. Sometimes you want to modify a local copy of the parameter. Then, taking it by ref-to-const actually prevents compiler optimizations like copy elisions. See the link to Dave's blog I gave in my answer. – sellibitze Oct 14 '09 at 16:06
  • 4
    Also, what about `long double`? `long long` (or `__int64`, or whatever your implementation's 64-bit integral type is)? pointers to members (which normally consist of 3 machine words)? iterators? allocators? It's more complicated than this... – Pavel Minaev Oct 14 '09 at 16:15
  • 2
    What about templates where you don't know the size of T! – Martin York Oct 14 '09 at 18:15
  • 1
    For function templates it's no big deal, because they pretty much always are `inline`; therefore, you just use `const T&`, and let the compiler optimize it as it sees fit when inlining the call. – Pavel Minaev Oct 14 '09 at 20:24
  • 11
    @Alexey, all fundamental types should be the exception to this rule, not only `double`. – Kirill V. Lyadvinsky Oct 15 '09 at 04:27
  • 1
    Including all non-class compound types, like `void*`. I would say, all non-class types. That seems to cover all the basics, like enums, integers, floating points, etc. – Johannes Schaub - litb Oct 15 '09 at 09:48
  • @Martin York: Define `argument::type`, specialize for `sizeof < sizeof(void*)`; `argument` can independently be specialized. Only loss: Template Argument Deduction. – MSalters Oct 15 '09 at 10:20
  • 1
    MSalters: That's a big loss at times. –  Dec 25 '09 at 12:52
  • 5
    What if I have a class that only stores a tiny data member but which performs some costly operation in its copy constructor? Or which does not have a copy constructor altogether? I don't think this rule of thumb makes any sense – Manuel Feb 09 '10 at 16:07
  • x86 platform: Use const T & arg if sizeof(T)>=12 and use T arg if sizeof(T) < 12. 12 = double + int = 3 * int = 3 * float – KindDragon Jan 28 '13 at 18:22
21

They do different things. const T& makes the function take a reference to the variable. On the other hand, T arg will call the copy constructor of the object and passes the copy. If the copy constructor is not accessible (e.g. it's private), T arg won't work:

class Demo {
    public: Demo() {} 
    private: Demo(const Demo& t) { } 
};

void foo(Demo t) { }

int main() {
    Demo t;
    foo(t); // error: cannot copy `t`.
    return 0;
}

For small values like primitive types (where all matters is the contents of the object, not the actual referential identity; say, it's not a handle or something), T arg is generally preferred. For large objects and objects that you can't copy and/or preserving referential identity is important (regardless of the size), passing the reference is preferred.

Another advantage of T arg is that since it's a copy, the callee cannot maliciously alter the original value. It can freely mutate the variable like any local variables to do its work.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • You may want to read this discussion: http://stackoverflow.com/questions/1561007/c-error-when-have-private-copy-ctor-with-public-assignment-operator sometimes (namely with temporary objects) having private copy constructor may fail even function calls with const& (at least for c++98). – Oren S Oct 14 '09 at 15:57
  • Oren: That's true but that's a side issue. It's not a consequence of function calls. As you said, it's the problem of not being able to have references to temporaries. – Mehrdad Afshari Oct 14 '09 at 16:15
  • 1
    The term "POD" is apparently misused. POD is not associated with small types in any way. POD values can be arbitrarily large. An array of 10000 'int' is a POD. The author probably meant to say "scalar types" or "basic types" instead of "POD types". – AnT stands with Russia Oct 14 '09 at 17:09
  • AndreyT: Thanks for pointing out. I specifically meant to say things like "int", "double", ... will edit. – Mehrdad Afshari Oct 14 '09 at 17:11
16

Taken from Move constructors. I like the easy rules

  1. If the function intends to change the argument as a side effect, take it by reference/pointer to a non-const object. Example:

    void Transmogrify(Widget& toChange);
    void Increment(int* pToBump);
    
  2. If the function doesn't modify its argument and the argument is of primitive type, take it by value. Example:

    double Cube(double value);
    
  3. Otherwise

    3.1. If the function always makes a copy of its argument inside, take it by value.

    3.2. If the function never makes a copy of its argument, take it by reference to const.

    3.3. Added by me: If the function sometimes makes a copy, then decide on gut feeling: If the copy is done almost always, then take by value. If the copy is done half of the time, go the safe way and take by reference to const.

In your case, you should take the int by value, because you don't intend to modify the argument, and the argument is of primitive type. I think of "primitive type" as either a non-class type or a type without a user defined copy constructor and where sizeof(T) is only a couple of bytes.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 3.3. If the function sometimes makes a copy of its argument ... ? – Steve Jessop Oct 14 '09 at 16:14
  • I've not included that because that uses his framework (for emulating "move constructors") :) If it takes a copy sometimes, i would say to decide on gut feeling (e.g how often is a copy made inside? how costly is it?) For example, for operator= that has constant swap (copy/swap idiom), i would take it by a value, because i would only seldomly not copy it (self assignment). – Johannes Schaub - litb Oct 14 '09 at 16:21
  • (see @Jerry Coffin's excellent answer and linked article for why taking operator= arg by value is a good thing) – Johannes Schaub - litb Oct 14 '09 at 16:22
  • This should be in every book out there – Vinícius Oct 31 '19 at 16:49
8

There's a popular advice that states that the method of passing ("by value" vs "by const reference") should be chosen depending in the actual size of the type you are going to pass. Even in this discussion you have an answer labeled as "correct" that suggests exactly that.

In reality, basing your decision on the size of the type is not only incorrect, this is a major and rather blatant design error, revealing a serious lack of intuition/understanding of good programming practices.

Decisions based on the actual implementation-dependent physical sizes of the objects must be left to the compiler as often as possible. Trying to "tailor" your code to these sizes by hard-coding the passing method is a completely counterproductive waste of effort in 99 cases out of 100. (Yes, it is true, that in case of C++ language, the compiler doesn't have enough freedom to use these methods interchangeably - they are not really interchangeable in C++ in general case. Although, if necessary, a proper size-based [semi-]automatic passing methios selection might be implemented through template metaprogramming; but that's a different story).

The much more meaningful criterion for selecting the passing method when you write the code "by hand" might sound as follows:

  1. Prefer to pass "by value" when you are passing an atomic, unitary, indivisible entity, such as a single non-aggregate value of any type - a number, a pointer, an iterator. Note that, for example, iterators are unitary values at the logical level. So, prefer to pass iterators by value, regardless of whether their actual size is greater than sizeof(void*). (STL implementation does exactly that, BTW).

  2. Prefer to pass "by const reference" when you are passing an aggregate, compound value of any kind. i.e. a value that has exposed pronouncedly "compound" nature at the logical level, even if its size is no greater than sizeof(void*).

The separation between the two is not always clear, but that how things always are with all such recommendations. Moreover, the separation into "atomic" and "compound" entities might depend on the specifics of your design, so the decision might actually differ from one design to the other.

Note, that this rule might produce decisions different from those of the allegedly "correct" size-based method mentioned in this discussion.

As an example, it is interesing to observe, that the size-based method will suggest you manually hard-code different passing methods for different kinds of iterators, depending on their physical size. This makes is especially obvious how bogus the size-based method is.

Once again, one of the basic principles from which good programming practices derive, is to avoid basing your decisions on physical characteristics of the platform (as much as possible). Instead, you decisions have to be based on the logical and conceptual properties of the entities in your program (as much as possible). The issue of passing "by value" or "by reference" is no exception here.


In C++11 introduction of move semantics into the language produced a notable shift in the relative priorities of different parameter-passing methods. Under certain circumstances it might become perfectly feasible to pass even complex objects by value

Should all/most setter functions in C++11 be written as function templates accepting universal references?

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • "...revealing a serious lack of intuition/understanding of good programming practices". Have a look for "JSF AV++" and then checkout rule 116. This standard was written by the US military for their Joint Strike Fighter with some big names involved. I am not sure they'd consider that they lacked intuition/understanding of good programming practices!!! – Richard Corden Oct 19 '09 at 18:35
  • 2
    @Richard Corden: It is completely irrlevant whether they lacked that understanding or not, since I'm sure it wasn't a priority. The reasoning they used was most likely completely orthogonal to any "good programming practices". The purpose of their standard was to make best use of the strictly defined set of tools in a highly specific (or, should I say, exotic) environment, which has very little to do with general application-level C++ programming and the good practices that apply there. – AnT stands with Russia Oct 19 '09 at 21:44
  • ... Reading the document more, I basically see what I expected to see in a military-spec document. This is a highly specialized coding standard intentionally tailored to a set of specific, fairly exotic, circumstances. Quite a few of the "rules" presented there make very strong "anti-rules" for general C++ programming under "normal" circumstances. – AnT stands with Russia Oct 19 '09 at 21:51
  • I' don't agree with your assessment here. There are some domain specific rules here, the clearest being no exceptions, but there are also very general rules. For example there are rules about identifier choice and file contents. The pass by value/by reference discussion is a pure performance concern, it has nothing to do with the domain nor does it have a safety case. I really don't think you can dismiss this as being "tailored for exotic circumstances". Re your second point - can you give examples of the "anti-rules" (other than no exceptions which actually has a good safety case)? – Richard Corden Oct 20 '09 at 09:58
  • So this answer says "What others say is incorrect. Use my advice instead, which is correct, and I'll not explain why. And yeah, my advice doesn't work in c++. Well actually it does, with template metaprogramming, and I'll not show how.". Quite a few words to say nothing useful! – anatolyg Feb 01 '15 at 12:39
  • @anatolyg: Your comment is mush shorter, yet somehow contains a lot more nonsensical assertions and self-contradictions. "my advice doesn't work in c++" - really? Where did I say that? "works with template metaprogramming, and I'll not show how" - this actually applied to the size-based approach, which I advice *against*. Why would I suddenly bother to "show how" then? When you don't understand something, it is better to ask for explanations than launch confused attacks. – AnT stands with Russia Feb 01 '15 at 16:49
  • I didn't follow you there - sorry! Still, I think this answer is wrong, because it assumes that "pass by value" and "pass by const reference" are conceptually different. You can exploit the fact that c++ has two mechanisms to do the same, and encode some conceptual information on your types in that, but it's not what everyone is doing, so it will not make your code more readable. – anatolyg Feb 01 '15 at 17:11
5

Contrary to popular and long-held beliefs, passing by const reference isn't necessarily faster even when you're passing a large object. You might want to read Dave Abrahams recent article on this very subject.

Edit: (mostly in response to Jeff Hardy's comments): It's true that passing by const reference is probably the "safest" alternative under the largest number of circumstances -- but that doesn't mean it's always the best thing to do. But, to understand what's being discussed here, you really do need to read Dave's entire article quite carefully, as it is fairly technical, and the reasoning behind its conclusions is not always intuitively obvious (and you need to understand the reasoning to make intelligent choices).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    This is true *if you're going to make a copy anyway*. If not, you're still better off passing by reference, because passing an lvalue will create a copy you don't actually need. – Jeff Hardy Oct 14 '09 at 15:49
  • 1
    That article is about _returning_ objects by copy vs. returning them in a _non-const reference_ parameter in _C++1x_. This obviously doesn't apply here. – sbi Oct 14 '09 at 15:56
  • 2
    Yes, of course there are limitations on it -- which is why 1) I said "necessarily", and 2) referred people to the article for full details... – Jerry Coffin Oct 14 '09 at 15:56
  • 2
    @sbi: you apparently didn't read the whole article. It's not strictly about returning objects -- that's simply what he uses as the initial example. – Jerry Coffin Oct 14 '09 at 15:59
  • @Jerry: in my experience, people tend not to read the details - especially on something as subtle as copy elision and the RVO. Unfortunately, subtle idioms require very precise, detailed explanations. – Jeff Hardy Oct 14 '09 at 16:05
  • @sbi: Not, the article is NOT exclusivly about returning objects. – sellibitze Oct 14 '09 at 16:16
3

Usually for built-in types you can just pass by value. They're small types.

For user defined types (or templates, when you don't what is going to be passed) prefer const&. The size of a reference is probably smaller than the size of the type. And it won't incurr an extra copy (no call to a copy constructor).

Leandro T. C. Melo
  • 3,974
  • 22
  • 22
  • Absolutely - the cost of copying (and of later destroying that copy) are key. A very small non-POD class can have expensive copy construction and destruction. e.g. a c_Binary_Tree class might have sizeof(void*) - a single member pointing to the root node - yet the copy constructor might copy the whole binary tree. std::map etc are probably close to this, though instances (in typical implementations) probably hold pointers to the begin and end nodes as well as the root, making them 3*sizeof(void*). –  Oct 14 '09 at 16:29
  • Incidentally, one method *must* have a const reference parameter irrespective of how cheap copy construction is - the copy constructor itself. Otherwise - well, the parameter is a copy so you need to call the copy constructor to set it up, and the copy constructor needs a copy as a parameter, so you need another copy constructor call to set that up, so you need another copy as that calls parameter, so you... –  Oct 14 '09 at 16:33
  • enums are user defined types, too. So instead of saying "user-defined" I'd say "class types" – sellibitze Oct 14 '09 at 20:12
2

Well, yes ... the other answers about efficiency are true. But there's something else going on here which is important - passing a class by value creates a copy and, therefore, invokes the copy constructor. If you're doing fancy stuff there, it's another reason to use references.

Sean
  • 509
  • 4
  • 12
  • 1
    Depends on the actual type and your compiler. Is it a trivially copiable type like a POD? Is your compiler able to elide copies? "...invokes the copy ctor..." is not a guarantee. It can be optimized away in some cases. – sellibitze Oct 14 '09 at 16:27
2

A reference to const T is not worth the typing effort in case of scalar types like int, double, etc. The rule of thumb is that class-types should be accepted via ref-to-const. But for iterators (which could be class-types) we often make an exception.

In generic code you should probably write "T const&" most of the time to be on the safe side. There's also boost's call traits you can use to select the most promising parameter passing type. It basically uses ref-to-const for class types and pass-by-value for scalar types as far as I can tell.

But there are also situations where you might want to accept parameters by value, regardless of how expensive creating a copy can be. See Dave's article "Want Speed? Use pass by value!".

ololuki
  • 377
  • 1
  • 7
  • 14
sellibitze
  • 27,611
  • 3
  • 75
  • 95
1

For simple types like int, double and char*, it makes sense to pass it by value. For more complex types, I use const T& unless there is a specific reason not to.

The cost of passing a 4 - 8 byte parameter is as low as you can get. You don't buy anything by passing a reference. For larger types, passing them by value can be expensive.

Brad
  • 861
  • 5
  • 11
1

It won't make any difference for an int, as when you use a reference the memory address still has to be passed, and the memory address (void*) is usually about the size of an integer.

For types that contain a lot of data it becomes far more efficient as it avoids the huge overhead from having to copy the data.

Yacoby
  • 54,544
  • 15
  • 116
  • 120
0

Well the difference between the two doesn't really mean much for ints.

However, when using larger structures (or objects), the first method you used, pass by const reference, gives you access to the structure without need to copy it. The second case pass by value will instantiate a new structure that will have the same value as the argument.

In both cases you see this in the caller

myFunct(item);

To the caller, item will not be changed by myFunct, but the pass by reference will not incur the cost of creating a copy.

There is a very good answer to a similar question over at Pass by Reference / Value in C++

Community
  • 1
  • 1
Flame
  • 2,166
  • 2
  • 20
  • 40
0

The difference between them is that one passes an int (which gets copied), and one uses the existing int. Since it's a const reference, it doesn't get changed, so it works much the same. The big difference here is that the function can alter the value of the int locally, but not the const reference. (I suppose some idiot could do the same thing with const_cast<>, or at least try to.) For larger objects, I can think of two differences.

First, some objects simply can't get copied, auto_ptr<>s and objects containing them being the obvious example.

Second, for large and complicated objects it's faster to pass by const reference than to copy. It's usually not a big deal, but passing objects by const reference is a useful habit to get into.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
-1

Either works fine. Don't waste your time worrying about this stuff.

The only time it might make a difference is when the type is a large struct, which might be expensive to pass on the stack. In that case, passing the arg as a pointer or a reference is (slightly) more efficient.

JSBձոգչ
  • 40,684
  • 18
  • 101
  • 169
  • 3
    If you're passing a multi-megabyte vector (or other container), passing by reference is going to make more than a slight difference. The vector itself is tiny (sizeof(void*) * 3, usually), but the copy-ctor will kill performance. – Jeff Hardy Oct 14 '09 at 15:47
-1

The problem appears when you are passing objects. If you pass by value, the copy constructor will be called. If you haven't implemented one, then a shallow copy of that object will be passed to the function.

Why is this a problem? If you have pointers to dynamically allocated memory, this could be freed when the destructor of the copy is called (when the object leaves the function's scope). Then, when you re call your destructor, youll have a double free.

Moral: Write your copy constructors.

Tom
  • 43,810
  • 29
  • 138
  • 169
  • 2
    If your class frees memory in the destructor, and has the default copy-constructor, then it is broken. It's true that this causes problems with pass-by-value, but it causes all sorts of problems elsewhere too. The solution isn't to avoid passing broken classes by value, the solution is not to write broken classes :-) – Steve Jessop Oct 14 '09 at 15:50
  • Let me rephrase and alter your moral: Try to design your classes so the compiler-generated copy/assign/dtor does the right thing in most cases. Provide custom functions for as few classes as possible. Then, you've applied RAII correctly. – sellibitze Oct 14 '09 at 16:14