c++c++11one-definition-ruleforwarding-referencepass-by-const-reference

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.


Solution

  • 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.