Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing literal as a const ref parameter

Imagine the following simplified code:

#include <iostream>
void foo(const int& x) { do_something_with(x); }

int main() { foo(42); return 0; }

(1) Optimizations aside, what happens when 42 is passed to foo?

Does the compiler stick 42 somewhere (on the stack?) and pass its address to foo?

(1a) Is there anything in the standard that dictates what is to be done in this situation (or is it strictly up to the compiler)?


Now, imagine slightly different code:

#include <iostream>
void foo(const int& x) { do_something_with(x); }

struct bar { static constexpr int baz = 42; };

int main() { foo(bar::baz); return 0; }

It won't link, unless I define int bar::baz; (due to ODR?).

(2) Besides ODR, why can't the compiler do whatever it did with 42 above?


An obvious way to simplify things is to define foo as:

void foo(int x) { do_something_with(x); }

However, what would one do in case of a template? Eg:

template<typename T>
void foo(T&& x) { do_something_with(std::forward<T>(x)); }

(3) Is there an elegant way to tell foo to accept x by value for primitive types? Or do I need to specialize it with SFINAE or some such?

EDIT: Modified what happens inside foo as it's irrelevant to this question.

like image 507
Innocent Bystander Avatar asked Aug 11 '17 23:08

Innocent Bystander


People also ask

How do I pass a const reference?

When you pass by const reference, you take the argument in by reference (avoiding making any copies of it), but cannot make any changes to the original object (much as would happen when you would take the parameters in by value).

Why can't you make a non-const reference to a literal?

A non-const reference cannot point to a literal. You cannot bind a literal to a reference to non-const (because modifying the value of a literal is not an operation that makes sense) and only l-values can be bound to references to non-const.

Can reference variable be const?

Such a reference is called an lvalue reference to a const value (sometimes called a reference to const or a const reference). In the above program, we bind const reference ref to modifiable lvalue x . We can then use ref to access x , but because ref is const, we can not modify the value of x through ref .

How do you pass a constant string in C++?

The data type const string& literally means “a reference to a string object whose contents will not be changed.” 1. Pass by value - a copy of the original object is created and passed. To pass a string by value, you just use the data type string.


1 Answers

Does the compiler stick 42 somewhere (on the stack?) and pass its address to foo?

A temporary object of type const int is created, initialized with the prvalue expression 42, and bound to the reference.

In practice, if foo is not inlined, that requires allocating space on the stack, storing 42 into it, and passing the address.

Is there anything in the standard that dictates what is to be done in this situation (or is it strictly up to the compiler)?

[dcl.init.ref].

Besides ODR, why can't the compiler do whatever it did with 42 above?

Because according to the language, the reference is bound to the object bar::baz, and unless the compiler knows exactly what foo is doing at the point where it is compiling the call, then it has to assume that this is significant. For example, if foo contains an assert(&x == &bar::baz);, that must not fire with foo(bar::baz).

(In C++17, baz is implicitly inline as a constexpr static data member; no separate definition is required.)

Is there an elegant way to tell foo to accept x by value for primitive types?

There is generally not much point in doing this in the absence of profiling data showing that pass-by-reference is actually causing problems, but if you really need to do it for some reason, adding (possibly SFINAE-constrained) overloads would be the way to go.

like image 74
T.C. Avatar answered Oct 07 '22 11:10

T.C.