Subtle question about function template partial ordering

32 views
Skip to first unread message

Brian Bi

unread,
Jan 11, 2018, 11:39:06 PM1/11/18
to std-dis...@isocpp.org
At Stack Overflow (https://stackoverflow.com/q/48219096/481267) the following code is being discussed:

template<typename T>
struct C {
    template<typename U>
    void operator +(U);
};
struct D: C<D> {}; struct E {}; template<typename T> void operator +(C<T>&, E); void F() { D d; E e; d + e; }

The overload resolution here seems to hinge on the question of how exactly the transformed function types are produced in the partial ordering test ([temp.func.order]/3).

For the member overload, if we consider the actual template in question to be

template <typename U> C<D>::operator+(U)

then the partial ordering process must fail to effect a choice (there is an implicit C<D>& parameter). On the other hand, if we consider it to be

template <typename T> template <typename U> C<T>::operator+(U)

then the non-member is more specialized (there is an implicit C<T>& parameter) and must win overload resolution.

I believe that the former interpretation is the correct one, because if we follow the standard's general philosophy that an X template is only a blueprint for an X and not an X itself, then it also follows that a function template member of a class template is not actually a function template, and therefore the partial ordering process, which orders "function templates", must take as input the function template member of the specialization C<D>. However, this argument is obviously not a very strong one.

  1. Is there some wording in the standard that tells us which interpretation is correct?
  2. If not, this is obviously a defect. If so, is the fact that GCC and Clang diverge evidence that this is not clear enough, so some more wording needs to be added anyway?

--
Brian Bi

Edward Catmur

unread,
Jan 12, 2018, 3:19:58 PM1/12/18
to ISO C++ Standard - Discussion


On Friday, 12 January 2018 04:39:06 UTC, Brian Bi wrote:
At Stack Overflow (https://stackoverflow.com/q/48219096/481267) the following code is being discussed:

template<typename T>
struct C {
    template<typename U>
    void operator +(U);
};
struct D: C<D> {}; struct E {}; template<typename T> void operator +(C<T>&, E); void F() { D d; E e; d + e; }

The overload resolution here seems to hinge on the question of how exactly the transformed function types are produced in the partial ordering test ([temp.func.order]/3).

For the member overload, if we consider the actual template in question to be

template <typename U> C<D>::operator+(U)

then the partial ordering process must fail to effect a choice (there is an implicit C<D>& parameter). On the other hand, if we consider it to be

template <typename T> template <typename U> C<T>::operator+(U)

then the non-member is more specialized (there is an implicit C<T>& parameter) and must win overload resolution.

I believe that the former interpretation is the correct one, because if we follow the standard's general philosophy that an X template is only a blueprint for an X and not an X itself, then it also follows that a function template member of a class template is not actually a function template, and therefore the partial ordering process, which orders "function templates", must take as input the function template member of the specialization C<D>. However, this argument is obviously not a very strong one.

  1. Is there some wording in the standard that tells us which interpretation is correct?
There is a hint in the example to [temp.func.order]/3 ("The declaration of B​::​operator* is transformed into the equivalent of..."). Given that gcc rejects this example (it is supposed to accept it) it seems pretty clear that the bug is with gcc (and also MSVC).

Consider also that the implicit C<D>& parameter may not be a dependent parameter at all! We can fully specialize C on D, and then 
 
template<typename T> struct C;
struct D;
template<> struct C<D> {
// etc ...

the candidate C<D>::template<class U> operator+(U) is per [temp.func.order]/3 equivalent to a free template<class U> operator+(C<D>&, U). But gcc rejects the latter as ambiguous and accepts the former as worse than your template<class T> operator+(C<T>&, U).
  1. If not, this is obviously a defect. If so, is the fact that GCC and Clang diverge evidence that this is not clear enough, so some more wording needs to be added anyway?
 
--
Brian Bi
Reply all
Reply to author
Forward
0 new messages