Which function should get chosen

Discussion in 'C++' started by Kaba, Aug 6, 2009.

  1. Kaba

    Kaba Guest

    Hi,

    In the following is a short code snippet concerning the overload
    resolution with function templates. Which function should be chosen in
    this case (i.e. what should the program return)?

    template <typename Type>
    class Base {};

    template <typename Type>
    class A : public Base<Type> {};

    template <typename Type>
    int f(const Base<Type>& that) {return 0;}

    template <typename Type>
    int f(const Type& that) {return 1;}

    int main()
    {
    A<int> a;
    return f(a);
    }

    I am surprised to see that both Visual Studio 2008 and Comeau C++ both
    choose the unrestricted template (return 1). I'd argue that the other
    one containing Base<Type> parameter is more specialized. What is going
    on?

    --
    http://kaba.hilvi.org
    Kaba, Aug 6, 2009
    #1
    1. Advertising

  2. Kaba

    Greg Herlihy Guest

    On Aug 6, 11:53 am, Kaba <> wrote:
    >
    > In the following is a short code snippet concerning the overload
    > resolution with function templates. Which function should be chosen in
    > this case (i.e. what should the program return)?
    >
    > template <typename Type>
    > class Base {};
    >
    > template <typename Type>
    > class A : public Base<Type> {};
    >
    > template <typename Type>
    > int f(const Base<Type>& that) {return 0;}
    >
    > template <typename Type>
    > int f(const Type& that) {return 1;}
    >
    > int main()
    > {
    >     A<int> a;
    >     return f(a);
    > }
    >
    > I am surprised to see that both Visual Studio 2008 and Comeau C++ both
    > choose the unrestricted template (return 1). I'd argue that the other
    > one containing Base<Type> parameter is more specialized. What is going
    > on?


    The compiler does not even consider the function template with the
    Base<Type> parameter, because a call to that function would require
    converting the argument to a base class template-id (specifically,
    converting the A<int> argument to a B<int> parameter).

    Now, according to §14.8.2.1/3, this type of conversion is allowed:

    "If P is a class, and P has the form template-id, then A can be a
    derived class of the deduced A."

    There is however one significant restriction:

    "These alternatives are considered only if type deduction would
    otherwise fail."

    Since there is another f() function template for which type deduction
    does succeed - the compiler does even consider the other f() overload
    when resolving the f() function call.

    Greg
    Greg Herlihy, Aug 6, 2009
    #2
    1. Advertising

  3. Kaba

    Kaba Guest

    Greg Herlihy wrote:
    > "If P is a class, and P has the form template-id, then A can be a
    > derived class of the deduced A."
    >
    > "These alternatives are considered only if type deduction would
    > otherwise fail."


    Thank you. I then have a related problem. I would like the following to
    compile. However, the 0 is ambiguated because it can also be converted
    to the null pointer:

    template <typename Type>
    class Base {};

    template <typename Type>
    class A
    {
    public:
    A() {}
    explicit A(const Type& that) {}
    explicit A(const Type* that) {}

    template <typename ThatType>
    explicit A(const Base<ThatType>& that) {}
    };

    int main()
    {
    A<float> a(0);
    return 0;
    }

    I do not want to use a cast to disambiguate in the constructor call.
    Ideally, of course, the 0 shouldn't denote a null-pointer value but
    there should be a special symbol for it: but's that's history. How do I
    get around this?

    This gives a context for the first post: I tried to use a template to
    catch the type of the parameter. But that broke the Base constructor.

    --
    http://kaba.hilvi.org
    Kaba, Aug 6, 2009
    #3
  4. Kaba

    Kaba Guest

    Kaba wrote:
    > template <typename ThatType>
    > explicit A(const Base<ThatType>& that) {}


    Sorry, this constructor should have been implicit:

    template <typename ThatType>
    A(const Base<ThatType>& that) {}

    --
    http://kaba.hilvi.org
    Kaba, Aug 6, 2009
    #4
  5. Kaba

    Kaba Guest

    Kaba wrote:

    Ok, I was missing some more stuff. This should be a complete test case
    for what I want to achieve. The constructors for which I pass a number
    should run the A(const Type& that) constructor.

    template <typename Type>
    class Base {};

    template <typename Type>
    class C : public Base<Type> {};

    template <typename Type>
    class A
    {
    public:
    A() {}
    explicit A(const Type& that) {}
    explicit A(const Type* that) {}

    template <typename ThatType>
    A(const Base<ThatType>& that) {}
    };

    int main()
    {
    C<float> a;
    A<float> b(a);
    A<float> c(0);
    A<float> d(0.0);

    A<int> e(0);
    A<int> f(0.0);

    return 0;
    }

    --
    http://kaba.hilvi.org
    Kaba, Aug 6, 2009
    #5
  6. Kaba

    Kaba Guest

    Kaba, Aug 6, 2009
    #6
  7. Kaba

    James Kanze Guest

    On Aug 6, 9:24 pm, Greg Herlihy <> wrote:
    > On Aug 6, 11:53 am, Kaba <> wrote:
    > > In the following is a short code snippet concerning the
    > > overload resolution with function templates. Which function
    > > should be chosen in this case (i.e. what should the program
    > > return)?


    > > template <typename Type>
    > > class Base {};


    > > template <typename Type>
    > > class A : public Base<Type> {};


    > > template <typename Type>
    > > int f(const Base<Type>& that) {return 0;}


    > > template <typename Type>
    > > int f(const Type& that) {return 1;}


    > > int main()
    > > {
    > > A<int> a;
    > > return f(a);
    > > }


    > > I am surprised to see that both Visual Studio 2008 and
    > > Comeau C++ both choose the unrestricted template (return 1).
    > > I'd argue that the other one containing Base<Type> parameter
    > > is more specialized. What is going on?


    > The compiler does not even consider the function template with
    > the Base<Type> parameter, because a call to that function
    > would require converting the argument to a base class
    > template-id (specifically, converting the A<int> argument to a
    > B<int> parameter).


    > Now, according to §14.8.2.1/3, this type of conversion is allowed:


    > "If P is a class, and P has the form template-id, then A can
    > be a derived class of the deduced A."


    > There is however one significant restriction:


    > "These alternatives are considered only if type deduction
    > would otherwise fail."


    > Since there is another f() function template for which type
    > deduction does succeed - the compiler does even consider the
    > other f() overload when resolving the f() function call.


    I don't think that's right. The compiler applies type deduction
    to both function templates, deducing Type == int for the first,
    and Type == A<int> for the second. It then applies overload
    resolution on the resulting functions: f( Base<int> const& )
    (from the first template) and f( A< int > const& ) (from the
    second template). Since the latter is a better match, it is
    chosen. The choice of the "more specialize" is only used as a
    tie breaker, if there would otherwise be ambiguity. That's not
    the case here.

    --
    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, Aug 7, 2009
    #7
  8. Kaba

    James Kanze Guest

    On Aug 6, 10:18 pm, Kaba <> wrote:
    > Thank you. I then have a related problem. I would like the
    > following to compile. However, the 0 is ambiguated because it
    > can also be converted to the null pointer:


    > template <typename Type>
    > class Base {};


    > template <typename Type>
    > class A
    > {
    > public:
    > A() {}
    > explicit A(const Type& that) {}
    > explicit A(const Type* that) {}


    > template <typename ThatType>
    > explicit A(const Base<ThatType>& that) {}
    > };


    > int main()
    > {
    > A<float> a(0);
    > return 0;
    > }


    > I do not want to use a cast to disambiguate in the constructor
    > call. Ideally, of course, the 0 shouldn't denote a
    > null-pointer value but there should be a special symbol for
    > it: but's that's history. How do I get around this?


    Give the exact type. There's no other solution. In this case:
    A<float> a( 0.0F ) ;

    --
    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, Aug 7, 2009
    #8
    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. Michelle Stone
    Replies:
    0
    Views:
    413
    Michelle Stone
    Oct 15, 2003
  2. Quasimido CSS
    Replies:
    2
    Views:
    6,596
    Quasimido CSS
    Nov 7, 2005
  3. Skip Montanaro

    Propagating poorly chosen idioms

    Skip Montanaro, Apr 5, 2005, in forum: Python
    Replies:
    3
    Views:
    269
    Roy Smith
    Apr 6, 2005
  4. Replies:
    1
    Views:
    253
    Ben Finney
    Oct 23, 2006
  5. sloan
    Replies:
    0
    Views:
    619
    sloan
    Jul 3, 2007
Loading...

Share This Page