1

I want to know what might be the possible advantages of passing by value over passing by const reference for primitive types like int, char, float, double, etc. to function? Is there any performance benefit for passing by value? Example:

int sum(const int x,const int y);

or

int sum(const int& x,const int& y);

For the second case, I have hardly seen people using this. I know there is benefit of passing by reference for big objects.

4
  • 1
    In your example the values are probably smaller than the references and there's no indirection. IMO there's no point passing a const reference to a primitive type. Using const on parameters passed by value bugs me too. No reason not to be able to modify those if the need arises. Commented May 14, 2021 at 22:59
  • 1
    @RetiredNinja Making a variable const by default is a code style to avoid unexpected modifications. It's considered so helpful that it's the default in modern languages like Rust. It's even a rule in the C++ Core Guidelines Commented May 14, 2021 at 23:32
  • 1
    @jabaa Your link specifically states: "Exception: Function arguments are rarely mutated, but also rarely declared const. To avoid confusion and lots of false positives, don’t enforce this rule for function arguments.". The arguments passed (by value) to main are not const either. Commented May 14, 2021 at 23:53
  • 1
    @dxiv As I said it's a common code style. Const parameters can be considered pedantic but it's the default in some modern languages. The parameters of main were specified long before we had the time to get all the experience and to write the C++ core guidelines. I know companies where a developer has to explain in a code review why a variable is mutable and IMHO that's a step in the right direction. const is the default. Commented May 14, 2021 at 23:54

2 Answers 2

3

In every ABI I know of, references are passed via something equivalent to pointers. So when the compiler cannot inline the function or otherwise must follow the ABI, it will pass pointers there.

Pointers are often larger than values; but more importantly, pointers do not point at registers, and while the top of the stack is almost always going to be in cache, what it points at may not. In addition, many ABIs have primitives passed via register, which can be faster than via memory.

The next problem is within the function. Whenever the code flow could possible modify an int, data from a const int& parameter must be reloaded! While the reference is to const, the data it refers to can be changed via other paths.

The most common ways this can happen is when you leave the code the complier can see while understanding the function body or modify memory through a global variable, or follow a pointer to touch an int elsewhere.

In comparison, an int argument whose address is not taken cannot be legally modified through other means than directly. This permits the compiler to understand it isn't being mutated.

This isn't just a problem for the complier trying to optimize and getting confused. Take something like:

struct ui{
  enum{ defFontSize=9;};
  std:optional<int> fontSize;
  void reloadFontSize(){
    fontSize=getFontSizePref();
    fontSizeChanged(*fontSize),
  }
  void fontSizeChanged(int const& sz){
    if(sz==defFontSize)
      fontSize=std:nullopt;
    else
      fontSize=sz;
    drawText(sz);
  }
  void drawText(int sz){
    std::cout << "At size  " << sz <<"\n";
  }
};

and the optional, to whom we are passing a reference, gets destroyed and used after destruction.

A bug like this can be far less obvious than this. If we defaulted to passing by value, it could not happen.

Sign up to request clarification or add additional context in comments.

Comments

1

Typically, primitive types are not passed by reference, but sometimes there is a point in that. E.g, on x64 machine long double is 16 bytes long and pointer is 8 bytes long. So it will be a little bit better to use a reference in this case.

In your example, there is no point in that: usual int is 4 bytes long, so you can pass two integers instead of one pointer.

You can use sizeof() to measure the size of the type.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.