44

Consider the following class template 'X' and its partial specializations.

template <class ...Types>
struct X {};               // #1

template <class T1>
struct X<T1> {};           // #2

template <class T1, class ...Types>
struct X<T1, Types...> {}; // #3

X<int> x;                  // #2 or #3 ?

I suspect X<int> is ambiguous. It is because:

It is obvious that both #2 and #3 are more specialized than #1, #2 and #3 are now compared. According to 14.5.5.2, let's consider which of the following #2' and #3' is more specialized.

template <class T1>
void f(X<T1>);             // #2'

template <class T1, class ...Types>
void f(X<T1, Types...>);   // #3'

According to 14.8.2.4, the first step is the template argument deduction using #2' as the argument template and #3' as the parameter template. Given the only argument type is X<A1>, the deduced T1 is A1, and Types is empty.

A = X<A1>, P = X<T1, Types...>  =>  T1 = A1, Types = {}

The second step is done using #3' as the argument template and #2' as the parameter template. Given the only argument type is X<A1, Args...>, according to 14.8.2.5/9 (note that this paragraph is recently revised by N3281), Args is simply ignored, the deduced T1 is A1 and argument deduction succeeds.

A = X<A1, Args...>, P = X<T1>  =>  T1 = A1 (Args is ignored)

Finally, the bidirectional argument deductions succeeded. So #2 is just as specialized as #3. In conclusion, X<int> is ambiguous.

My question is: "is my interpretation correct?"

If this interpretation is correct, the definition of 'std::common_type' in 20.9.7.6/3 is inappropriate.

template <class ...T>
struct common_type;            // #1

template <class T>
struct common_type<T>          // #2
{
    typedef T type;
};

template <class T, class U>
struct common_type<T, U>       // #3
{
    typedef
        decltype(true ? declval<T>() : declval<U>())
    type;
};

template <class T, class U, class ...V>
struct common_type<T, U, V...> // #4
{
    typedef typename
        common_type<typename common_type<T, U>::type, V...>::type
    type;
};

When common_type<A, B> is used, #3 and #4 are ambiguous.

Note: on the first example, GCC 4.7.0 (snapshot) and Clang 3.0 select #2. However, these compilers are so unreliable that they don't follow the other changes by N3281.

Constructor
  • 6,903
  • 2
  • 17
  • 55
iorate
  • 683
  • 5
  • 6
  • 1
    It looks like you are correct. An empty parameter pack should not affect partial ordering. gcc seems to ignore this and place a variadic template lower in the list, other things being equal. E.g. it compiles an example at 14.5.6.2/5 which is explicitly stated to be ambiguous. – n. 'pronouns' m. Dec 11 '11 at 05:41
  • Correctness is usually the subject of unit tests, especially in programming. This ensures that your interpretation is correct and that you understand the language specifications to a high degree of confidence, both of which may be in question here. – djhaskin987 Dec 16 '11 at 16:59
  • @n.m. Is [this code](http://ideone.com/AHJ9s) what you are talking about? I've taken that code from [N3242](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf), as linked from Wikipedia – Aaron McDaid Dec 20 '11 at 14:44
  • 14.5.6.2/5 (from N3242) "The presence of unused ellipsis and default arguments has no effect on the partial ordering of **function** templates" (my emphasis). Maybe this only applies to function templates, while this question is about class templates? – Aaron McDaid Dec 20 '11 at 14:52
  • @Aaron: no, [this one](http://ideone.com/W82pH). This is the last example of 14.5.6.2. It compiles though the comment plainly states it should not. – n. 'pronouns' m. Dec 20 '11 at 14:58
  • @Aaron: partial ordering of class templates is expressed in terms of partial ordering of function templates, as described in 14.5.5.2. So it must apply to both. – n. 'pronouns' m. Dec 20 '11 at 15:01
  • @n.m. In N3242 in 14.5.6.2/5 they do not say it shouldn't compile. They appear to have changed the example in the document you're looking at. – Aaron McDaid Dec 20 '11 at 15:06
  • (in other words, I'm probably looking at an obsolete document, and I'm no expert in this anyway!) – Aaron McDaid Dec 20 '11 at 15:08

1 Answers1

8

14.8.2.4, section 11 (I refer to draft N3242).

In most cases, all template parameters must have values in order for deduction to succeed, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered used. —end note ] [ Example:

template <class T> T f(int); // #1
template <class T, class U> T f(U); // #2
void g() {
f<int>(1); // calls #1
}

In your case, #3 will be used.

Björn Pollex
  • 70,106
  • 28
  • 177
  • 265
Mikhail Semenov
  • 943
  • 8
  • 8