4

When I run the program below, I get an error saying that more than one specialization matches the template argument list of class X<int, int*, 10>. The compiler obviously can't decide whether to use "Specialization 1" or "Specialization 2". I don't understand why that is the case because I thought that the compiler determines the level of specialization based on the number of template parameters. So, since "Specialization 1" has 2 template parameters (template<class T, int I>), and "Specialization 2" has only 1 template parameter (template<class T>) I figured that "Specialization 2" is more specialized than "Specialization 1". Obviously my reasoning is incorrect, so could someone explain the logic behind deducing which template is more specialized?


    #include <iostream>
    using namespace std;
    
    template<class T, class U, int I> struct X {
        void f() { cout << "General Template" << endl; }
    };
    
    template<class T, int I> struct X<T, T*, I> {
        void f() { cout << "Specialization 1" << endl; }
    };
    
    template<class T> struct X<int, T*, 10> {
        void f() { cout << "Specialization 2" << endl; }
    };
    
    int main() {
        X<int, int*, 10> f;
        f.f();
        return 0;
    }

  • 1
    Was it you that posted the exact same question just a little while ago? Don't do that! Deleting and reposting will be found out and is very frowned upon. If you had comments on the earlier question on how to improve the question, then you should have ***[edit]*** that question instead. – Some programmer dude Jan 24 '22 at 11:43
  • 1
    And please read [the help pages](http://stackoverflow.com/help), especially ["What topics can I ask about here?"](http://stackoverflow.com/help/on-topic) and ["What types of questions should I avoid asking?"](http://stackoverflow.com/help/dont-ask). Also take the [tour] and read about [ask] good questions and [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Jan 24 '22 at 11:43
  • No, I didn't post this question. My friend posted it and it was deleted, so I formulated the question with a little more detail thinking that was the issue. – Pavle Šarenac Jan 24 '22 at 11:44
  • 1
    please include the error message verbatim in the question. – 463035818_is_not_an_ai Jan 24 '22 at 12:39
  • 2
    fwiw the other quesiton https://stackoverflow.com/questions/70832792/how-do-i-know-which-template-is-more-specialized was not deleted. It was closed because it needs some improvement. Once it has been fixed it will be reopened – 463035818_is_not_an_ai Jan 24 '22 at 12:40
  • 1
    I am very sorry for my lapse, I thought that it was deleted. In the meantime, my friend deleted his question because he also got confused, so it would be great if someone could answer my question because I believe that it is clear enough. All the best! – Pavle Šarenac Jan 24 '22 at 12:58

1 Answers1

1

This topic is unfortunately very complicated! I'm basing myself on this answer and cppreference, while quoting from the current working draft of the standard.

Firstly, according to "Matching of partial specializations":

A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list, and the deduced template arguments satisfy the associated constraints of the partial specialization, if any.

In your example, the context is the statement X<int, int*, 10> f;, which matches both partial specializations:

template<class T, int I> struct X<T, T*, I> {...}    // -> T = int, I = 10
template<class T>        struct X<int, T*, 10> {...} // -> T = int

Since this is the case, further checking is required to determine whether one of these candidates is more specialized than the other. That is described under "Partial ordering of partial specializations":

For two partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates:

  • Each of the two function templates has the same template parameters and associated constraints as the corresponding partial specialization.
  • Each function template has a single function parameter whose type is a class template specialization where the template arguments are the corresponding template parameters from the function template for each template argument in the template-argument-list of the simple-template-id of the partial specialization.

That is very wordy, but as shown by the example it simply comes down to writing the partial specializations as function arguments:

template<class T, int I> void f(X<T, T*, I>);    // #1
template<class T>        void f(X<int, T*, 10>); // #2

From there we have to look at a different section about the "Partial ordering of function templates". Quoting the relevant paragraphs:

Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. [...]

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial].

In other words, transform #1 and #2 (again) by "instantiating" each with unique arguments, that is, different from any other arguments present:

void f(X<Ut1, Ut1*, Uv1>); // #A
void f(X<int, Ut2*, 10>);  // #B

And then use their "function type", being the return type and the argument types, to perform deduction according to yet another section, "Deducing template arguments during partial ordering":

The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.

Ok, going by the example on cppreference:

// #1 from #B: 
// void(X<T, T*, I>) from void(X<int, Ut2*, 10>): deduction fails
// #2 from #A:
// void(X<int, T*, 10>) from void(X<Ut1, Ut1*, Uv1>): deduction fails

At least, I think that's the case, and therefore the result of this whole ordeal would be that neither template is more specialized than the other.

sigma
  • 2,758
  • 1
  • 14
  • 18