tags:

views:

76

answers:

3

Hallo! I want to specialize std::max for a platform-specific fraction type.

The prototype for the system-specific max I want to use looks like this:

fract builtin_max(fract a, fract b);

My idea for a specialized std::max looks like this:

template <> inline
const fract& std::max<fract>(const fract& a, const fract& b) {
    return builtin_max(a, b);
}

However, the compiler understandably complains about returning a reference to a local temporary. Is there any way I can work around this?

+3  A: 

You could return a result by value. Then RVO most likely will take place. In the end you will get behavior as you wanted.

And I'd change builtin_max declaration to

fract builtin_max(const fract& a, const fract& b);

It will help to the compiler to recognize the possibility for RVO.

Finally your max will look like:

template <> 
fract std::max<fract>(const fract& a, const fract& b) {
    return builtin_max(a, b);
}
Kirill V. Lyadvinsky
I tried that, but the compiler does not support it.
kotlinski
@kotlinski Does it mean that fract has no copy constructor? Or your problem is that RVO does not take place?
SadSido
It has a copy constructor. My guess is that either RVO does not take place, or the compiler doesn't account for it when emitting warnings.
kotlinski
If you are to modify the `builtin_max` you might as well have it return a reference --the maximum of two elements IS one of the two elements-- and then everything will fall right in place...
David Rodríguez - dribeas
+1  A: 

If you can modify the builtin_max then the way to go is changing the signature so that it takes two constant references and returns a constant reference (unless the type is small enough that pass-by-value makes sense), and overload for the non-const version of it. Then you can adapt the signatures the easy way: just forward the call.

fract const & builtin_max( fract const & lhs, fract const & rhs );
fract & builtin_max( fract & lhs, fract & rhs );

template <>
fract const & std::max( fract const & lhs, fract const & rhs ) {
   return builtin_max( lhs, rhs );
}
template <>
fract & std::max( fract & lhs, fract & rhs ) {
   return builtin_max( lhs, rhs );
}

Another simple thing that you can do is not overload std::max but rather produce your own max function within your namespace. All unqualified uses of max that take fract values will find your max function before they try to use the std::max default template. Then again, this will not work for fully qualified calls to std::max:

namespace x {
   class fract;
   fract max( fract lhs, fract rhs ) { return builtin_max( lhs, rhs ); }
}
// force a link time error if fully qualified std::max is used (undefined)
// instead of silently getting std::max to use < (if it is defined)
// if there is no operator<( fract const &, fract const & ), leave this out
// to get an earlier compile time error
template <> fract const & std::max( fract const &, fract const & );
template <> fract & std::max( fract &, fract & );

int main() {
   using namespace std;
   fract a,b;
   max( a, b );          // x::max
   // std::max( a, b )   // ouch, link time error
}
David Rodríguez - dribeas
A: 

My solution was to do it just like this:

fract std::max(fract a, fract b) {
    return builtin_max(a, b);
}

Super simple, and works just like I want it :)

kotlinski