Forcing all function template parameters to deduce the same type

Discussion in 'C++' started by Dan Krantz, Jun 13, 2006.

  1. Dan Krantz

    Dan Krantz Guest

    I have the following template to ensure that a given number (val) falls into
    a range (between vmin & vmax):

    template<typename T> T ForceNumericRange( const T& val, const T& vmin, const
    T& vmax)
    {
    T retVal = val;

    if ( retVal < vmin )
    retVal = vmin;
    else if ( retVal > vmax )
    retVal = vmax;

    return retVal;
    }

    Here's the call:

    float comm;
    ....
    comm = ForceNumericRange( comm, 0.0, 100.0);

    The compiler (Visual Age C++ 5 under AIX 4.3) complains: The function
    template parameter "T" has been deduced to have two values: "float" and
    "double"

    When I took the .0 off the constant parameters, it complained about T being
    float & int. I solved the problem with this change:

    comm = ForceNumericRange( comm, 0.0F, 100.0F);

    but it's hokey, since I don't want the caller to have to explicitly specify
    the type for the range constants.

    Is there a way to declare the template such that the types of the 2nd and
    3rd parameter are always deduced from the type of the first argument? I'm
    pretty sure I could give parameters 2 & 3 a different type declaration, and
    rely on runtime conversions between the different types, but I'd like this
    resolved at compile time; also, that approach might fail if vmin was a
    variable and vmax was a constant.

    Any suggestions?
     
    Dan Krantz, Jun 13, 2006
    #1
    1. Advertising

  2. Dan Krantz

    Steve Pope Guest

    Dan Krantz <> wrote:

    >I have the following template to ensure that a given number (val) falls into
    >a range (between vmin & vmax):


    >template<typename T> T ForceNumericRange( const T& val, const T& vmin, const
    >T& vmax)
    >{
    > T retVal = val;
    >
    > if ( retVal < vmin )
    > retVal = vmin;
    > else if ( retVal > vmax )
    > retVal = vmax;
    >
    > return retVal;
    >}
    >
    >Here's the call:
    >
    >float comm;
    >...
    >comm = ForceNumericRange( comm, 0.0, 100.0);


    >The compiler (Visual Age C++ 5 under AIX 4.3) complains: The function
    >template parameter "T" has been deduced to have two values: "float" and
    >"double"


    >When I took the .0 off the constant parameters, it complained about T being
    >float & int. I solved the problem with this change:
    >
    >comm = ForceNumericRange( comm, 0.0F, 100.0F);
    >
    >but it's hokey, since I don't want the caller to have to explicitly specify
    >the type for the range constants.


    >Is there a way to declare the template such that the types of the 2nd and
    >3rd parameter are always deduced from the type of the first argument?


    Not that I can think of.

    My two comments, neither of which answers your question, is that it is
    uncommon to want to use float instead of double, except when
    doing I/O or packing data or similar; and that using const

    Steve
    to define constants, instead of placing literals in function
    arguments, is useful.
     
    Steve Pope, Jun 13, 2006
    #2
    1. Advertising

  3. In article <e6ktl2$ebj$>,
    "Dan Krantz" <> wrote:

    > I have the following template to ensure that a given number (val) falls into
    > a range (between vmin & vmax):
    >
    > template<typename T> T ForceNumericRange( const T& val, const T& vmin, const
    > T& vmax)
    > {
    > T retVal = val;
    >
    > if ( retVal < vmin )
    > retVal = vmin;
    > else if ( retVal > vmax )
    > retVal = vmax;
    >
    > return retVal;
    > }
    >
    > Here's the call:
    >
    > float comm;
    > ...
    > comm = ForceNumericRange( comm, 0.0, 100.0);
    >
    > The compiler (Visual Age C++ 5 under AIX 4.3) complains: The function
    > template parameter "T" has been deduced to have two values: "float" and
    > "double"
    >
    > When I took the .0 off the constant parameters, it complained about T being
    > float & int. I solved the problem with this change:
    >
    > comm = ForceNumericRange( comm, 0.0F, 100.0F);
    >
    > but it's hokey, since I don't want the caller to have to explicitly specify
    > the type for the range constants.
    >
    > Is there a way to declare the template such that the types of the 2nd and
    > 3rd parameter are always deduced from the type of the first argument? I'm
    > pretty sure I could give parameters 2 & 3 a different type declaration, and
    > rely on runtime conversions between the different types, but I'd like this
    > resolved at compile time; also, that approach might fail if vmin was a
    > variable and vmax was a constant.
    >
    > Any suggestions?


    You can use "concepts" to implement "mixed-mode" arithmetic functions.
    The first thing you need is a C++03 emulation of concepts:

    template <bool, class T = void> struct where {};
    template <class T> struct where<true, T> {typedef T type;};

    This is more famously known as "enable_if" and can be found at
    www.boost.org. I've also called it "restrict_to" in the past. But it's
    the same beast whatever you call it.

    With this struct, and maybe a little help from std::tr1::type_traits (or
    boost::type_traits) you can constrain your template parameters. In this
    case you could make all three arguments different template parameters
    (say T, U and V), but constrain U and V such that they are both
    convertible to T. That would look like:

    #include <tr1/type_traits>

    template <bool, class T = void> struct where {};
    template <class T> struct where<true, T> {typedef T type;};

    template<typename T, class U, class V>
    typename where
    <
    std::tr1::is_convertible<U, T>::value &&
    std::tr1::is_convertible<V, T>::value,
    T
    >::type

    ForceNumericRange( const T& val, const U& vmin, const V& vmax)
    {
    T retVal = val;

    if ( retVal < vmin )
    retVal = vmin;
    else if ( retVal > vmax )
    retVal = vmax;

    return retVal;
    }

    Other constraints might work for you as well. For example you might
    want to constrain each of T, U and V to be arithmetic types:

    template<typename T, class U, class V>
    typename where
    <
    std::tr1::is_arithmetic<T>::value &&
    std::tr1::is_arithmetic<U>::value &&
    std::tr1::is_arithmetic<V>::value,
    T
    >::type

    ForceNumericRange( const T& val, const U& vmin, const V& vmax);

    There is a big push on the standards committee to make concepts part of
    the language for C++0X. This would clean up the syntax and make it
    easier to build and reuse complex constraints. If accepted, it will
    probably use "where" as a keyword. So if you use "where" now, note that
    it may not compile in the future. You can view this as either a feature
    or a bug. When it doesn't compile, you'll know it is time to upgrade to
    the language supported variant.

    Finally, you could go with 3 unconstrained templates:

    template<typename T, class U, class V>
    T
    ForceNumericRange( const T& val, const U& vmin, const V& vmax);

    This is what I refer to as "overly generic" code and is prone to
    breakage in the case that the function name is part of an overload set
    (if ForceNumericRange is overloaded, even in other namespaces). With a
    name like ForceNumericRange, unintended overloading seems unlikely. But
    it is easy to fall into the trap of using overly generic coding
    techniques with very common names (as was done in the standard):

    distance
    advance
    copy
    fill
    rotate

    -Howard
     
    Howard Hinnant, Jun 13, 2006
    #3
  4. In article
    <>,
    Howard Hinnant <> wrote:

    > You can use "concepts" to implement "mixed-mode" arithmetic functions.
    > The first thing you need is a C++03 emulation of concepts:
    >
    > template <bool, class T = void> struct where {};
    > template <class T> struct where<true, T> {typedef T type;};


    Oh, I got so carried away with concepts that I neglected to give you one
    of the simplest techniques. :)

    You can do exactly this:

    > Is there a way to declare the template such that the types of the 2nd and
    > 3rd parameter are always deduced from the type of the first argument?


    with the following code:

    template <class T>
    struct identity
    {
    typedef T type;
    };

    template<typename T>
    T
    ForceNumericRange(const T& val, const typename identity<T>::type& vmin,
    const typename identity<T>::type& vmax);

    I.e. this puts the second and third arguments into a non-deducible
    context. And you still have the option of further constraining T using
    "where" if necessary.

    -Howard
     
    Howard Hinnant, Jun 13, 2006
    #4
    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. George2

    template function argument deduce

    George2, Mar 11, 2008, in forum: C Programming
    Replies:
    0
    Views:
    375
    George2
    Mar 11, 2008
  2. Ed
    Replies:
    1
    Views:
    400
    James Kanze
    Aug 14, 2008
  3. Maik
    Replies:
    2
    Views:
    998
  4. nguillot
    Replies:
    5
    Views:
    535
  5. Chris McAce
    Replies:
    1
    Views:
    440
    88888 Dihedral
    Feb 8, 2012
Loading...

Share This Page