Bug 47765 - [Core/1391] Wrong template deduction
Summary: [Core/1391] Wrong template deduction
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.5.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-02-16 11:51 UTC by Denis Kolesnikov
Modified: 2020-05-08 15:38 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2011-09-08 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Denis Kolesnikov 2011-02-16 11:51:39 UTC
The following code fails to compile neither with g++ version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5) nor with 4.5.1:


template<typename ItT>
struct traits {
        typedef typename ItT::value_type value_t;
};

template<typename ItT>
struct A {
        typedef typename traits<ItT>::value_t value_t;
};

template<typename T>
struct B {
        typedef T type_t;
};

struct C {

        template<typename T, typename T2>
        void foo(const A<T>& r) {}

        template<typename T>
        void foo(const B<T>& r) {}
};

void foo()
{
        B<char> b;
        C c;
        c.foo(b);
        c.foo<char>(b); // fails to compile
}
Comment 1 Daniel Krügler 2011-09-08 09:20:26 UTC
This problem seems still to exist in gcc 4.7.0 20110903 (experimental)
Comment 2 Paolo Carlini 2011-09-08 10:51:29 UTC
Let's ask Jason what he thinks about this...
Comment 3 Jason Merrill 2011-09-08 16:15:24 UTC
14.8.2/6: At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.

So, when we start to try to evaluate b.foo<char>, we substitute 'char' for T in both templates, so they become

        template<typename T2>
        void foo(const A<char>& r) {}

        void foo(const B<char>& r) {}

14.8.1/6: Implicit conversions (Clause 4) 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. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified.

So we no longer do deduction on the first parameter, but rather check for a conversion.  What is unclear in the standard is when exactly the conversion should happen.  Currently G++ and EDG seem to check for a conversion during argument deduction, which leads to instantiation of A<char> and thus the error you see.  But another reasonable interpretation would be to skip the parameter during deduction and then let normal overload resolution check for the conversion; in that case deduction would fail for the first foo and so we never check the conversion, so we don't try to instantiate A<char>.  clang accepts this testcase, so it seems likely that this is what they are doing.
Comment 4 Daniel Krügler 2011-09-08 16:57:48 UTC
(In reply to comment #3)

In fact I expected that there is some implementation freedom which allows this (I was thinking of 14.7.1 p6), but I still wonder: I have always understood that 14.8.2 only applies, if *all* template arguments are provided:

p1: "When a function template specialization is referenced, all of the template arguments shall have values."

p5: "[..] When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values. [..]"

Doesn't this indicate that

template<typename T, typename T2>
void foo(const A<T>& r);

must be rejected immediately in this case, because T2 is nowhere specified nor deducible?
Comment 5 Jason Merrill 2011-09-08 18:50:32 UTC
(In reply to comment #4)
> In fact I expected that there is some implementation freedom which allows this
> (I was thinking of 14.7.1 p6), but I still wonder: I have always understood
> that 14.8.2 only applies, if *all* template arguments are provided:

"This is done at the beginning of template argument deduction when any
explicitly specified template arguments are substituted into the function type" seems pretty clear to me that it happens at the beginning and does not require all parameters to have explicit arguments.

> Doesn't this indicate that
> 
> template<typename T, typename T2>
> void foo(const A<T>& r);
> 
> must be rejected immediately in this case, because T2 is nowhere specified nor
> deducible?

No, it seems to me that the standard specifies that we do deduction on all parm/arg pairs and only then fail if a template parameter is not deduced; we don't check ahead of time to see if any parameters are nondeducible.  That might be a reasonable change given that now some semantics depend on whether or not a parameter is used in a deducible context, but it isn't what the standard currently says.
Comment 6 Jason Merrill 2011-09-08 19:22:58 UTC
(In reply to comment #3)
> But another reasonable interpretation would be to skip the parameter
> during deduction and then let normal overload resolution check for the
> conversion; in that case deduction would fail for the first foo and so we never
> check the conversion, so we don't try to instantiate A<char>.

But that breaks several libstdc++ tests, and this testcase:

template<typename ItT>
struct A {
        typedef typename ItT::value_t value_t;
};

template<typename T>
struct B {
        typedef T type_t;
};

template <class T, class... U>
typename A<T>::value_t f(int, T, U...);

template <class T>
T f(T, T);

void foo()
{
        B<char> b;
        f(b, b);
}

because then we do the substitution before checking whether B<char> can convert to int.  I suppose that we could check conversion between deduction and substitution...I think I'm going to raise this with the committee.
Comment 7 Paolo Carlini 2013-05-25 08:51:04 UTC
Do we have a DR # for this issue?
Comment 8 Daniel Krügler 2013-05-25 09:18:55 UTC
(In reply to Paolo Carlini from comment #7)
> Do we have a DR # for this issue?

It seems to me that this is

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1391

submitted by Jason with a simplified form of the example discussed here.
Comment 9 Paolo Carlini 2013-05-25 11:07:48 UTC
Ah, nice. Thanks.
Comment 10 Marek Polacek 2020-05-08 15:38:14 UTC
Fixed in r223301.