Be careful when storing the result of std::clamp as a reference

Earlier, someone posted a question about std::clamp but the thread was removed before I had a chance to respond (probably because the OP realized his mistake).

The result was stored as a reference. It was OK in this case because all the arguments were variables but I just wanted to point out that one should be careful when storing the result of std::clamp as a reference (same with std::max/std::min) because if you instead pass literals (temporaries) then they would get destroyed at the end of the statement and you would end up with a dangling reference.

1
2
3
int number = 21;
const int &p = std::clamp(number, 10, 20);
std::cout << p << "\n"; // Undefined behaviour! (p refers to the temporary object created by the literal 20 which no longer exists) 

There were no real need to store the result as a reference, and I can't remember when I have ever needed it, so I would say it's probably best to avoid it. A situation where you might want to do it is if you're using it with a class type that cannot be copied or that is costly to copy.
Last edited on
So in this case, the lifetime of the object created by the int literal isn't extended due to the 'const T&' type of p?
This is indeed dangerous since std::clamp operates with (temporary) const &.

In cppreference.com they added a note regarding that problem. See:

https://en.cppreference.com/w/cpp/algorithm/clamp
Ganado wrote:
So in this case, the lifetime of the object created by the int literal isn't extended due to the 'const T&' type of p?

The lifetime is not extended.
A const ref only extends the lifetime if you bind it to the temporary directly.
It does not work with functions that return references.
Last edited on
There is one simple rule which helps to avoid such issues without knowing all the internals of the standard, which is that POD's should be treated as POD's.

for ex. POD data should not never be const by reference in function parameters since you gain 0 performance benefit from that.

for ex. POD data should not never be const by reference in function parameters since you gain 0 performance benefit from that.
No, look at code generated for this program:

Whoops, I misread your post.

I agree that you should sometimes pass POD by const reference ("not never")
But I'm not actually sure what you mean.

1
2
3
4
5
6
7
8
9
10
11
12
#include <type_traits>

struct A { unsigned char buffer[1 << 20]; } a;
static_assert(std::is_pod<A>::value); // note: deprecated
static_assert(std::is_standard_layout<A>::value && std::is_trivial<A>::value);

void f() 
{
  void by_value(A), by_reference(A const&);
  by_value(a); 
  by_reference(a);
}


a:
        .zero   1048576
f():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 1048576
        mov     rax, rsp
        mov     ecx, OFFSET FLAT:a
        mov     edx, 1048576
        mov     rsi, rcx
        mov     rdi, rax
        call    memcpy
        call    by_value(A)
        add     rsp, 1048576
        mov     edi, OFFSET FLAT:a
        call    by_reference(A const&)
        nop
        leave
        ret

Passing a by value incurs a call to memcpy.
https://godbolt.org/z/r9GrKxxcf
Last edited on
std::clamp always takes the arguments by reference and returns a reference no matter what type it is. It's not something we can change unless we write our own clamp function (template).
mbozzi,

I mean cases such as this:

void f(const int& param);

vs:

void f(int param);

How exactly is the first version better or faster?
it's a waste of keystrokes to write that const and &
it's just an int, it won't hurt if it gets copied and the compiler will optimize it anyway..

However:
void f(int& param);
Is a different story, obviously you need a reference here by design!
How exactly is the first version better or faster?

It isn't.

If the argument is expensive-to-copy it might make sense to pass it by const& to avoid that copy. Evidence above.

Note there is a double negative in this sentence which I am having trouble parsing:
malibor wrote:
for ex. POD data should not never be const by reference in function parameters since you gain 0 performance benefit from that.
We could be in agreement, I just don't understand that sentence.
The 'rule of thumb' is to use const ref where the size of the data to be passed is greater than the size of a pointer - as behind the scene refs are passed as pointers. So for 64-bit, anything with a size less 9 bytes pass by value otherwise pass by const ref.
mbozzi wrote:
We could be in agreement, I just don't understand that sentence.

My mistake because in language grammar POD's aren't just int's, floats and similar but also structs which may do things such as your example, but I was referring to built in data types.

@seeplus, that's good rule, It's easy to remember it.
A struct is not a pod. There is even a std::is_pod. See:

https://en.cppreference.com/w/cpp/types/is_pod

Since passing by const reference is that similar to passing by value i would guess that the compiler may generate the same binary for both (whatever is shorter: the value or the reference/pointer).
Some structs are PODs.
Topic archived. No new replies allowed.