std::max(short,long) doesn't work

Discussion in 'C++' started by Phil Endecott, Oct 18, 2007.

  1. Dear Experts,

    I'm surprised to find that std::max doesn't work (i.e. won't compile) if
    the arguments are not of exactly the same type, e.g. one is a short and
    the other is a long:

    #include <algorithm>

    int f(short s, long l) {
    return std::max(s,l);
    }

    $ g++ -W -Wall -c /tmp/maxtest.cc
    /tmp/maxtest.cc: In function ‘int f(short int, long int)’:
    /tmp/maxtest.cc:5: error: no matching function for call to ‘max(short
    int&, long int&)’

    I can't even compare a long with an integer constant.

    Presumably this is a consequence of these functions returning
    references, not values; you can't return a reference to something that
    could be a short or could be a long, and promoting to the larger size
    and returning a reference to the temporary would have its own issues.

    Any thoughts? Would it be possible to write further overloads for min
    and max functions that match different argument types return values not
    references?


    Regards,

    Phil.
    Phil Endecott, Oct 18, 2007
    #1
    1. Advertising

  2. On Oct 18, 9:19 pm, Phil Endecott <>
    wrote:
    > Dear Experts,
    >
    > I'm surprised to find that std::max doesn't work (i.e. won't compile) if
    > the arguments are not of exactly the same type, e.g. one is a short and
    > the other is a long:
    >


    thats because the template is defined to take two arguments of same
    type

    > #include <algorithm>
    >
    > int f(short s, long l) {
    > return std::max(s,l);
    >
    > }
    >
    > $ g++ -W -Wall -c /tmp/maxtest.cc
    > /tmp/maxtest.cc: In function 'int f(short int, long int)':
    > /tmp/maxtest.cc:5: error: no matching function for call to 'max(short
    > int&, long int&)'
    >
    > I can't even compare a long with an integer constant.


    Yes you can, just explicitly provide template arguments:
    std::max<long>(s,l);
    Alternatively, do an explicit cast : std::max((long)s,l);

    -Neelesh
    Neelesh Bodas, Oct 18, 2007
    #2
    1. Advertising

  3. Hi Neelesh, thanks for the quick reply.

    Neelesh Bodas wrote:
    > On Oct 18, 9:19 pm, Phil Endecott <>
    > wrote:
    >> Dear Experts,
    >>
    >> I'm surprised to find that std::max doesn't work (i.e. won't compile) if
    >> the arguments are not of exactly the same type, e.g. one is a short and
    >> the other is a long:
    >>

    >
    > thats because the template is defined to take two arguments of same
    > type


    Indeed, but I'm surpised that the short isn't promoted to a long as it
    would be for a non-template function where both arguments have the same
    type:

    int f(long x, long y) {
    return 1;
    }

    int g() {
    short s;
    long l;
    return f(s,l);
    }

    I'm not saying that anything is wrong - this just wasn't what I had
    (naively) expected.

    > Yes you can, just explicitly provide template arguments:
    > std::max<long>(s,l);


    Ah, that's interesting. So if I provide an explicit type then it
    behaves like my non-template function f above.


    Thanks,

    Phil.
    Phil Endecott, Oct 18, 2007
    #3
  4. On Oct 18, 9:34 pm, Phil Endecott <>
    wrote:
    > Hi Neelesh, thanks for the quick reply.
    >
    > Neelesh Bodas wrote:
    > > On Oct 18, 9:19 pm, Phil Endecott <>
    > > wrote:
    > >> Dear Experts,

    >
    > >> I'm surprised to find that std::max doesn't work (i.e. won't compile) if
    > >> the arguments are not of exactly the same type, e.g. one is a short and
    > >> the other is a long:

    >
    > > thats because the template is defined to take two arguments of same
    > > type

    >
    > Indeed, but I'm surpised that the short isn't promoted to a long as it
    > would be for a non-template function where both arguments have the same
    > type:
    >


    14.8.1(4) from the standard:
    "Implicit conversions will be performed on a function argument to
    convert it to the type of the corresponding function parameter if the
    parameter type contains no template parameters
    that participate in template argument deduction"

    In the current case since both s and l participate in template
    argument deduction, s won't be promoted to the type of l.

    > int f(long x, long y) {
    > return 1;
    >
    > }
    >
    > int g() {
    > short s;
    > long l;
    > return f(s,l);
    >
    > }
    >
    > I'm not saying that anything is wrong - this just wasn't what I had
    > (naively) expected.
    >
    > > Yes you can, just explicitly provide template arguments:
    > > std::max<long>(s,l);

    >
    > Ah, that's interesting. So if I provide an explicit type then it
    > behaves like my non-template function f above.


    If you provide the explicit type, then s and l are no more used in
    template argument deduction. Hence s can be safely promoted to the
    desired type (long in this case).

    As another example, this will also work: std::max<int>(s,l); . In this
    case, both s and l will undergo standard conversion to int. This is
    possible since neither of them participate in template argument
    deduction.


    -N
    Neelesh Bodas, Oct 18, 2007
    #4
  5. In article <JWLRi.11899$>,
    Phil Endecott <> wrote:

    > Hi Neelesh, thanks for the quick reply.
    >
    > Neelesh Bodas wrote:
    > > On Oct 18, 9:19 pm, Phil Endecott <>
    > > wrote:
    > >> Dear Experts,
    > >>
    > >> I'm surprised to find that std::max doesn't work (i.e. won't compile) if
    > >> the arguments are not of exactly the same type, e.g. one is a short and
    > >> the other is a long:
    > >>

    > >
    > > thats because the template is defined to take two arguments of same
    > > type

    >
    > Indeed, but I'm surpised that the short isn't promoted to a long as it
    > would be for a non-template function where both arguments have the same
    > type:
    >
    > int f(long x, long y) {
    > return 1;
    > }
    >
    > int g() {
    > short s;
    > long l;
    > return f(s,l);
    > }
    >
    > I'm not saying that anything is wrong - this just wasn't what I had
    > (naively) expected.
    >
    > > Yes you can, just explicitly provide template arguments:
    > > std::max<long>(s,l);

    >
    > Ah, that's interesting. So if I provide an explicit type then it
    > behaves like my non-template function f above.


    If you use it this be forewarned that you should not catch the return
    value as a const reference (const long&), but catch it as a long
    instead. If you catch it as a reference:

    const long& m = std::max<long>(s,l);

    then you risk having a reference to a destructed temporary (the
    temporary long created from converting from s).

    Here is a more robust (and more flexible) min/max which does not have
    this danger, and is usable without specifying <long>:

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2199.html

    The std committee briefly considered this solution for standardization
    in C++0X but rejected it. However the reference implementation is there
    in the paper free for the taking.

    -Howard
    Howard Hinnant, Oct 18, 2007
    #5
  6. * Howard Hinnant:
    > In article <JWLRi.11899$>,
    > Phil Endecott <> wrote:
    >
    >> Hi Neelesh, thanks for the quick reply.
    >>
    >> Neelesh Bodas wrote:
    >>> On Oct 18, 9:19 pm, Phil Endecott <>
    >>> wrote:
    >>>> Dear Experts,
    >>>>
    >>>> I'm surprised to find that std::max doesn't work (i.e. won't compile) if
    >>>> the arguments are not of exactly the same type, e.g. one is a short and
    >>>> the other is a long:
    >>>>
    >>> thats because the template is defined to take two arguments of same
    >>> type

    >> Indeed, but I'm surpised that the short isn't promoted to a long as it
    >> would be for a non-template function where both arguments have the same
    >> type:
    >>
    >> int f(long x, long y) {
    >> return 1;
    >> }
    >>
    >> int g() {
    >> short s;
    >> long l;
    >> return f(s,l);
    >> }
    >>
    >> I'm not saying that anything is wrong - this just wasn't what I had
    >> (naively) expected.
    >>
    >>> Yes you can, just explicitly provide template arguments:
    >>> std::max<long>(s,l);

    >> Ah, that's interesting. So if I provide an explicit type then it
    >> behaves like my non-template function f above.

    >
    > If you use it this be forewarned that you should not catch the return
    > value as a const reference (const long&), but catch it as a long
    > instead. If you catch it as a reference:
    >
    > const long& m = std::max<long>(s,l);
    >
    > then you risk having a reference to a destructed temporary (the
    > temporary long created from converting from s).


    Possibly you meant something other than what you actually wrote.

    std::max returns the same type as the argument type.

    Therefore, in the example above it returns a 'long', not a 'long const&'.

    Therefore, the reference is not bound to an internal temporary in the
    call, but to a temporary that the compiler creates for just this
    purpose. And that temporary's life is extended to the end of the scope
    of the reference. It's all very safe.

    Are you by any chance referring to the reference implementation you link
    to below?

    I'm too lazy to check... :)


    > Here is a more robust (and more flexible) min/max which does not have
    > this danger, and is usable without specifying <long>:
    >
    > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2199.html
    >
    > The std committee briefly considered this solution for standardization
    > in C++0X but rejected it. However the reference implementation is there
    > in the paper free for the taking.



    Cheers,

    - Alf

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Oct 18, 2007
    #6
  7. * Alf P. Steinbach:
    > * Howard Hinnant:
    >> In article <JWLRi.11899$>,
    >> Phil Endecott <> wrote:
    >>
    >>> Hi Neelesh, thanks for the quick reply.
    >>>
    >>> Neelesh Bodas wrote:
    >>>> On Oct 18, 9:19 pm, Phil Endecott <>
    >>>> wrote:
    >>>>> Dear Experts,
    >>>>>
    >>>>> I'm surprised to find that std::max doesn't work (i.e. won't
    >>>>> compile) if
    >>>>> the arguments are not of exactly the same type, e.g. one is a short
    >>>>> and
    >>>>> the other is a long:
    >>>>>
    >>>> thats because the template is defined to take two arguments of same
    >>>> type
    >>> Indeed, but I'm surpised that the short isn't promoted to a long as
    >>> it would be for a non-template function where both arguments have the
    >>> same type:
    >>>
    >>> int f(long x, long y) {
    >>> return 1;
    >>> }
    >>>
    >>> int g() {
    >>> short s;
    >>> long l;
    >>> return f(s,l);
    >>> }
    >>>
    >>> I'm not saying that anything is wrong - this just wasn't what I had
    >>> (naively) expected.
    >>>
    >>>> Yes you can, just explicitly provide template arguments:
    >>>> std::max<long>(s,l);
    >>> Ah, that's interesting. So if I provide an explicit type then it
    >>> behaves like my non-template function f above.

    >>
    >> If you use it this be forewarned that you should not catch the return
    >> value as a const reference (const long&), but catch it as a long
    >> instead. If you catch it as a reference:
    >>
    >> const long& m = std::max<long>(s,l);
    >>
    >> then you risk having a reference to a destructed temporary (the
    >> temporary long created from converting from s).

    >
    > Possibly you meant something other than what you actually wrote.
    >
    > std::max returns the same type as the argument type.


    Dang, yes it does, but it adds "const&".

    Sorry.

    I was unable to believe something like that could have made it into the
    standard.


    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Oct 18, 2007
    #7
  8. In article <>,
    "Alf P. Steinbach" <> wrote:

    > Dang, yes it does, but it adds "const&".
    >
    > Sorry.
    >
    > I was unable to believe something like that could have made it into the
    > standard.


    <chuckle> Not only did we, but we confirmed that we wanted to keep it
    that way for C++0X! ;-)

    But I forgot to mention in my previous post that the reference
    implementation in N2199 uses rvalue-ref. I keep forgetting not everyone
    has that yet. :) If you want to play with it I recommend gcc 4.3 or
    conceptgcc:

    http://www.generic-programming.org/software/ConceptGCC/

    And fwiw, returning a reference isn't always a bad idea, especially a
    non-const one. Being able to write:

    int i = 4;
    int j = 2;
    max(i, j) = 3;

    can be very handy.

    It is only dangerous to return a reference (const or not) if one of the
    arguments is an rvalue. It is an easy mistake to make in interface
    design (one I've made myself).

    -Howard
    Howard Hinnant, Oct 18, 2007
    #8
  9. Phil Endecott

    James Kanze Guest

    On Oct 18, 6:34 pm, Phil Endecott <>
    wrote:
    > Hi Neelesh, thanks for the quick reply.


    > Neelesh Bodas wrote:
    > > On Oct 18, 9:19 pm, Phil Endecott <>
    > > wrote:


    > >> I'm surprised to find that std::max doesn't work (i.e. won't compile) if
    > >> the arguments are not of exactly the same type, e.g. one is a short and
    > >> the other is a long:


    > > thats because the template is defined to take two arguments of same
    > > type


    > Indeed, but I'm surpised that the short isn't promoted to a
    > long as it would be for a non-template function where both
    > arguments have the same type:


    > int f(long x, long y) {
    > return 1;
    > }


    > int g() {
    > short s;
    > long l;
    > return f(s,l);
    > }


    And how should the compiler know that it isn't the opposite
    which is wanted. In the above case, you've specified exactly
    that there is a single f. With the template, the compiler
    could, potentially, instantiate either max(long,long) or
    max(short,short). (If you'd written f(short, short) above, it
    would also have worked.)

    > I'm not saying that anything is wrong - this just wasn't what
    > I had (naively) expected.


    Templates rarely do:).

    > > Yes you can, just explicitly provide template arguments:
    > > std::max<long>(s,l);


    > Ah, that's interesting. So if I provide an explicit type then it
    > behaves like my non-template function f above.


    If you provide a specific type, the compiler doesn't have to
    deduce it. The problem only occurs because the compiler can't
    deduce the type when the two arguments have different types.

    There have been many different attempts to provide a max/min
    which actually work as expected, but it's far from trivial.
    Especially as expectations vary:). For what you want, you'd
    need something like:

    template< typename T1, typename T2 >
    typename ReturnType< T1, T2 >::type
    max( T1 x, T2 y )
    {
    return x > y ? x : y ;
    }

    Where ReturnType is something like:

    template< typename T1, typename T2 > class ReturnType ;
    template< typename T >
    class ReturnType< T, T >
    {
    public:
    typedef T type ;
    } ;

    // Lot's of specializations for mixed types...

    Others want max to use only references, so you can write
    something like:

    max(a,b) = 0 ;

    This requires something like:

    template< typename T >
    T& max( T& a, T& b );

    I'm not sure what overload resolution would do if you have both
    (although a few simple examples seem to work with g++).

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Oct 19, 2007
    #9
  10. Howard Hinnant wrote:

    >>> Yes you can, just explicitly provide template arguments:
    >>> std::max<long>(s,l);

    >> Ah, that's interesting. So if I provide an explicit type then it
    >> behaves like my non-template function f above.

    >
    > If you use it this be forewarned that you should not catch the return
    > value as a const reference (const long&), but catch it as a long
    > instead. If you catch it as a reference:
    >
    > const long& m = std::max<long>(s,l);
    >
    > then you risk having a reference to a destructed temporary (the
    > temporary long created from converting from s).


    And g++ doesn't seem to give a warning in this case, which is a bit
    disconcerting as it normally does give some sort of message if you do
    something bad with a temporary.


    Phil.
    Phil Endecott, Oct 19, 2007
    #10
  11. Phil Endecott

    James Kanze Guest

    On Oct 19, 12:34 pm, Phil Endecott
    <> wrote:
    > Howard Hinnant wrote:
    > >>> Yes you can, just explicitly provide template arguments:
    > >>> std::max<long>(s,l);
    > >> Ah, that's interesting. So if I provide an explicit type then it
    > >> behaves like my non-template function f above.


    > > If you use it this be forewarned that you should not catch the return
    > > value as a const reference (const long&), but catch it as a long
    > > instead. If you catch it as a reference:


    > > const long& m = std::max<long>(s,l);


    > > then you risk having a reference to a destructed temporary (the
    > > temporary long created from converting from s).


    > And g++ doesn't seem to give a warning in this case, which is a bit
    > disconcerting as it normally does give some sort of message if you do
    > something bad with a temporary.


    How can it? You're initializing a reference with a reference.
    At that point, it has no way of knowing anything about the
    lifetime of the object the reference refers to. And in the call
    to max, everything seems fine as well, since the lifetime of the
    temporary is longer than that of the reference argument. To
    catch the error and warn, it would have to trace the lifetimes
    and values through the function.

    Try turning up optimization. Maybe it warns then.
    (Optimization will cause additional flow analysis, particularly
    over the inlined functions, which are otherwise treated as
    separate functions.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Oct 20, 2007
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. marco_segurini
    Replies:
    3
    Views:
    2,284
    Karl Heinz Buchegger
    Jul 7, 2004
  2. George Marsaglia

    Assigning unsigned long to unsigned long long

    George Marsaglia, Jul 8, 2003, in forum: C Programming
    Replies:
    1
    Views:
    674
    Eric Sosman
    Jul 8, 2003
  3. David Geering

    longs, long longs, short short long ints . . . huh?!

    David Geering, Jan 8, 2007, in forum: C Programming
    Replies:
    15
    Views:
    558
    Keith Thompson
    Jan 11, 2007
  4. puzzlecracker
    Replies:
    3
    Views:
    1,766
    Mike Wahler
    May 8, 2006
  5. Summercool
    Replies:
    9
    Views:
    879
    dorayme
    Oct 23, 2007
Loading...

Share This Page