Thread: Help with template class

  1. #1
    Registered User
    Join Date
    Nov 2011
    Posts
    24

    Help with template class

    I wonder how to solve a problem that I have with a template function. The function definition looks like

    Code:
    template<typename T>
    T foo(T a, T b)
    {
        return a + b;
    }
    but this produces an error when I'm trying to compile a program with the code

    Code:
    foo(0, 0.5)
    The error message reads

    Code:
    error: no matching function for call to 'foo(int, double)'
    I tried to fix this by adding two more functions, so I had

    Code:
    template<typename T>
    T foo(T a, T b)
    {
        return a + b;
    }
    
    template<typename T>
    T foo(int a, T b)
    {
        return foo(T(a), b);
    }
    
    template<typename T>
    T foo(T a, int b)
    {
        return foo(a, T(b));
    }
    which solved the first compile error, but then the line

    Code:
    foo(0, 0)
    would not compile, giving me the message

    Code:
    error: call of overloaded 'foo(int, int)' is ambiguous
    candidates are: T foo(T, T) [with T = int]
    note:                 T foo(int, T) [with T = int]
    note:                 T foo(T, int) [with T = int]
    So, I'm looking for a way to make both foo(0, 0.5) and foo(0, 0) compile. Any ideas? Thanks in advance.

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Why not just call it as foo(0.0, 0.5)?

    If you do want to have two different types involved, then what do you want the return type to be?

    EDIT:
    By the way, you are implementing a function template.
    Last edited by laserlight; 04-22-2012 at 08:58 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Nov 2011
    Posts
    24
    Quote Originally Posted by laserlight View Post
    Why not just call it as foo(0.0, 0.5)?
    Simply because it's simpler to write foo(0, 0.5) :P If I define foo as

    Code:
    double foo(double a, double b)
    {
        return a + b;
    }
    then foo(0, 0.5) compiles, so I want it to compile even for my more general template function.

    Quote Originally Posted by laserlight View Post
    If you do want to have two different types involved, then what do you want the return type to be?
    If I have an integer type and a float type, I want the integer value to be converted to the float value. If both are float types but of different float types, I want the one with the lowest accuracy to be converted to the type with the higher accuracy.

    Actually, I don't want integer types at all, since originally, foo was a function that returned uniformly distributed random numbers (I just simplified it a bit before I started this thread). Hence, I check the type of the number using

    Code:
    STATIC_ASSERT(!std::numeric_limits<T>::is_integer);
    which will give a compile time error if T is an integer type but not if it isn't.

    I also have a separate function with a different name especially for integer arguments.

  4. #4
    Registered User gardhr's Avatar
    Join Date
    Apr 2011
    Posts
    151
    You could also either specify the template parameter explicitly (foo<double>(0, 0.5)) or else cast the integer literal to double. Laserlight's suggestion really makes the most sense, though. As they say...

  5. #5
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    Outside of specific situations it is better to do what laserlight suggested.

    However...

    Are you trying to do this for native types only?

    Are you trying to do this generally (any type)?

    Type discovery, which you'd need to know which type for the function to return, is only partially solved in C++98.

    C++11 can do the real thing in a couple of different ways.

    *shrug*

    In any event, you can always be explicit, by simply picking a type, where you are unaware of the parameter types.

    Code:
    foo<double>(0, 0.5)
    Soma

  6. #6
    Registered User
    Join Date
    Nov 2011
    Posts
    24
    Okay, thanks for the answer. I think I will just define the function explicitly for all types I am going to use it for, since it rather small, and skip the template.

  7. #7
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by phantomotap View Post
    C++11 can do the real thing in a couple of different ways.
    decltype ? Or were you referring to something else ?

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    As I said, there are a lot of ways you can do it.

    Soma

    Code:
    template
    <
        typename F1
      , typename F2
      , bool FUse1
      , bool FUse2
    >
    struct MGetOperatorConversionHelper
    {
        // raise a compiler error
    };
    
    template
    <
        typename F1
      , typename F2
    >
    struct MGetOperatorConversionHelper<F1, F2, true, false>
    {
        typedef F1 UResult;
    };
    
    template
    <
        typename F1
      , typename F2
    >
    struct MGetOperatorConversionHelper<F1, F2, false, true>
    {
        typedef F2 UResult;
    };
    
    template
    <
        typename F1
      , typename F2
    >
    struct MGetOperatorConversionHelper<F1, F2, true, true>
    {
        // raise a compiler error
    };
    
    template
    <
        typename F1
      , typename F2
    >
    struct MGetOperatorConversion
    {
        struct F1Result{unsigned int mUnused[16];};
        struct F2Result{unsigned int mUnused[64];};
        static F1Result GetOperatorConversion(F1);
        static F2Result GetOperatorConversion(F2);
        typedef typename MGetOperatorConversionHelper<F1, F2, (sizeof(F1Result) == sizeof(GetOperatorConversion(F1() + F2()))), (sizeof(F2Result) == sizeof(GetOperatorConversion(F1() + F2())))>::UResult UResult;
    };
    
    template
    <
        typename FTarget
    >
    struct MGetOperatorConversion<FTarget, FTarget>
    {
        typedef FTarget UResult;
    };
    
    template
    <
        typename F1
      , typename F2
    >
    decltype(F1() + F2()) DoSomething1 // Both `F1' and `F2' must have default constructors.
    (
        F1 f1
      , F2 f2
    )
    {
        return(f1 + f2);
    }
    
    template
    <
        typename F1
      , typename F2
    >
    auto DoSomething2
    (
        F1 f1
      , F2 f2
    ) -> decltype(f1 + f2) // Places fewer requirements on `F1' and `F2' while working on fewer compilers.
    {
        return(f1 + f2);
    }
    
    template
    <
        typename F1
      , typename F2
    >
    typename MGetOperatorConversion<F1, F2>::UResult DoSomething3 // Either `F1' or `F2' must be convertable to `F1' or `F2' as appropriate.
    (
        F1 f1
      , F2 f2
    )
    {
        return(f1 + f2);
    }
    
    #include <iostream>
    #include <typeinfo>
    
    int main()
    {
        std::cout << typeid(int).name() << ':' << typeid(DoSomething1(0, 5)).name() << '\n';
        std::cout << typeid(float).name() << ':' << typeid(DoSomething1(0, 5.0f)).name() << '\n';
        std::cout << typeid(double).name() << ':' << typeid(DoSomething1(0, 5.0)).name() << '\n';
        std::cout << typeid(int).name() << ':' << typeid(DoSomething2(0, 5)).name() << '\n';
        std::cout << typeid(float).name() << ':' << typeid(DoSomething2(0, 5.0f)).name() << '\n';
        std::cout << typeid(double).name() << ':' << typeid(DoSomething2(0, 5.0)).name() << '\n';
        std::cout << typeid(int).name() << ':' << typeid(DoSomething3(0, 5)).name() << '\n';
        std::cout << typeid(float).name() << ':' << typeid(DoSomething3(0, 5.0f)).name() << '\n';
        std::cout << typeid(double).name() << ':' << typeid(DoSomething3(0, 5.0)).name() << '\n';
        return(0);
    }
    Last edited by phantomotap; 04-22-2012 at 12:26 PM.

  9. #9
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by then
    Okay, thanks for the answer. I think I will just define the function explicitly for all types I am going to use it for, since it rather small, and skip the template.
    I guess that's fine but you're basically designing a function around a typing convenience. I don't know if this is the first time you've heard this, but C++ is a type strict language and it is best to type what you mean. A parameter list of two doubles means you should pass in two doubles. It's just a decimal point man.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 01-30-2011, 04:28 PM
  2. Template class nested in template class
    By yahn in forum C++ Programming
    Replies: 7
    Last Post: 04-18-2009, 11:40 AM
  3. Replies: 4
    Last Post: 11-01-2006, 02:23 PM
  4. Template <class T1, class T2, class T3> error LNK2019
    By JonAntoine in forum C++ Programming
    Replies: 9
    Last Post: 10-11-2004, 12:25 PM

Tags for this Thread