Re: Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- From: "Harvey" <harveyab@xxxxxxxx>
- Date: 27 Feb 2007 19:51:44 -0800
On Feb 27, 12:02 pm, "Doug Harrison [MVP]" <d...@xxxxxxxx> wrote:
On Tue, 27 Feb 2007 05:15:48 -0500, David Wilkinson <no-re...@xxxxxxxxxxxx>
wrote:
Not to beat a dead horse, but I would still follow what the standard
library does for min() and max(): use < only and const T&.
The reason to use < is because that is what many standard library
algorithms (like std::sort()) require to be defined. If you use > you
are placing a requirement on your class that is not required by the
standard library.
That's a good idea.
The reason to use const T& is that it will be much more efficient for a
complex type, particularly one that requires memory allocation in the
copy constructor. I would need detailed evidence that using T is more
efficient for simple types, and even if it was it would be extremely
unusual for it to really matter.
To my surprise, it does make a difference, though I agree it would be
really rare for it be significant. I compiled the following with /O2:
template <typename T>
inline
const T& Clamp1( const T& lower, const T& x, const T& upper )
{
if ( x < lower )
return lower;
if ( x > upper )
return upper;
return x;
}
template <typename T>
inline
T Clamp2( T lower, T x, T upper )
{
if ( x < lower )
return lower;
if ( x > upper )
return upper;
return x;
}
int f(int x, int y, int upper)
{
return Clamp1(x, y, upper);
}
int g(int x, int y, int upper)
{
return Clamp2(x, y, upper);
}
*********************************************************************
The VC8 output was for f was:
PUBLIC ?f@@YAHHHH@Z ; f
; Function compile flags: /Ogtpy
; COMDAT ?f@@YAHHHH@Z
_TEXT SEGMENT
_x$ = 8 ; size = 4
_y$ = 12 ; size = 4
_upper$ = 16 ; size = 4
?f@@YAHHHH@Z PROC ; f, COMDAT
; 25 : return Clamp1(x, y, upper);
mov eax, DWORD PTR _y$[esp-4]
cmp eax, DWORD PTR _x$[esp-4]
jge SHORT $LN4@f
lea eax, DWORD PTR _x$[esp-4]
mov eax, DWORD PTR [eax]
; 26 : }
ret 0
; 25 : return Clamp1(x, y, upper);
$LN4@f:
cmp eax, DWORD PTR _upper$[esp-4]
lea eax, DWORD PTR _upper$[esp-4]
jg SHORT $LN7@f
lea eax, DWORD PTR _y$[esp-4]
$LN7@f:
mov eax, DWORD PTR [eax]
; 26 : }
ret 0
?f@@YAHHHH@Z ENDP ; f
_TEXT ENDS
*********************************************************************
The VC8 output was for g was:
PUBLIC ?g@@YAHHHH@Z ; g
; Function compile flags: /Ogtpy
; COMDAT ?g@@YAHHHH@Z
_TEXT SEGMENT
_x$ = 8 ; size = 4
_y$ = 12 ; size = 4
_upper$ = 16 ; size = 4
?g@@YAHHHH@Z PROC ; g, COMDAT
; 30 : return Clamp2(x, y, upper);
mov ecx, DWORD PTR _y$[esp-4]
mov eax, DWORD PTR _x$[esp-4]
cmp ecx, eax
jl SHORT $LN5@g
mov eax, DWORD PTR _upper$[esp-4]
cmp ecx, eax
jg SHORT $LN5@g
mov eax, ecx
$LN5@g:
; 31 : }
ret 0
?g@@YAHHHH@Z ENDP ; g
_TEXT ENDS
The first one has three more instructions than the second and is presumably
slower, but in this day and age, I wouldn't bet on it. :) I'm just
surprised there's a difference when the Clamp functions are both inlined...
Ah, it is making the return type a reference that explains it. Make Clamp1
return T, and the assembly output is identical for f and g. That makes
sense.
If there is a higher-level downside to returning a const reference argument
by const reference, it is this:
const int& z = Clamp1(x, y, 100);
Now this requires creation of a temporary to hold the value 100, and if
Clamp1 returns this temporary (NB: by reference), z will be bound to it.
The problem is, this temporary will be destroyed at the end of the
full-expression, so z will end up a dangling reference. This can't happen
if you use Clamp2, because C++ requires its return value of type T to exist
as long as z exists. This is a relatively unlikely bug, and it shows why
it's important to watch what you're binding to const references. Another
example would be a ctor taking a const reference and initializing a member
const reference with it. Basically, you should avoid binding something to a
const reference if the reference lives beyond the full-expression that
performs the binding.
--
Doug Harrison
Visual C++ MVP
Well, I think I follow most of that... So I think I'll stay with pass
by value since:
1. it is shorter
2. it is not in a library
3. it avoids the lifetime problems
4. it is only used in a context that I have full control of
5. it is only used on simple integral scaler types
But I have made one more modification, sigh :-(
template <typename T>
T Clamp( T lower, T x, T upper )
{
if ( lower > upper )
return lower;
if ( lower > x )
return lower;
if ( x > upper )
return upper;
return x;
}
This gives the lower limit priority.
I had replaced:
m_Posx = x = max( m_Minx, min( m_Maxx-m_CRx, x ));
in my code with:
m_Posx = x = Clamp( m_Minx, x, m_Maxx-m_CRx );
and found m_Posx bouncing between two values
when m_CRx became larger than m_Maxx.
Like I said "Sigh".
Thanks again All, for all your help.
Harvey
.
- Follow-Ups:
- References:
- Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- From: Harvey
- Re: Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- From: Harvey
- Re: Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- From: David Wilkinson
- Re: Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- From: Doug Harrison [MVP]
- Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- Prev by Date: Re: Multilanguage support in MFC application
- Next by Date: Debug Assertion Failed Message in afx.inl at line122 & afshtml.inl file at line 57
- Previous by thread: Re: Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- Next by thread: Re: Function f(lb,x,ub); equivalent to min(ub,max(lb,x)); ?
- Index(es):
Relevant Pages
|