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.
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)?
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 acceptx
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.