Ambiguous Overload Templates

template<typename T1, size_t SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

You should use std::size_t instead of int. run here

Edit : Actually, your comments and my intuition about the code led me to dig into the topic. At first glance, a standard developer ( like me ) expect compiler to convert int to std::size_t(because they are both integral type and implicitly converting is very trivial) and select void foo(std::vector<std::array<T1, SIZE>> bar) as best specialization. So while reading template argument deduction page i found this :

If a non-type template parameter is used in the parameter list, and the corresponding template argument is deduced, the type of the deduced template argument ( as specified in its enclosing template parameter list, meaning references are preserved) must match the type of the non-type template parameter exactly, except that cv-qualifiers are dropped, and except where the template argument is deduced from an array bound—in that case any integral type is allowed, even bool though it would always become true:

As always, of course, you must read few more times than once to understand what it means :)

So an interesting result comes out.

Already our desired specialization is not selected but if the compiler had been forced to select, it would be an error.

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(b); // P = std::vector<std::array<int,(int)SIZE>
            // A = std::vector<std::array<int,(unsigned_long)SIZE>>
            // error: deduced non-type template argument does not have the same
            // type as its corresponding template argument */
}

run code

Another interesting thing is :

If the non-type template argument had not been deduced, there would be no restriction which forces argument and template types to be same.

#include <vector>
#include <array>
#include <iostream>

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo<int,3>(b);
}

run code


I think this is simply due to one line from [temp.deduct.call]/4

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A

To clarify, A means the parameter, from [temp.deduct.call]/1

...template argument deduction with the type of the corresponding argument of the call (call it A)...

As has already been pointed out, changing template<typename T1, int SIZE> to template<typename T1, size_t SIZE> fixes the issue you are seeing. As stated in [temp.deduct.call]/4, the compiler is looking to deduce an A that is identical to A. Since an std::array has template arguments <class T, size_t N> (from [array.syn]), it's second parameter is in fact size_t, not int.

Therefore, for the template deduction, your generic function of template<typename T1> is able to match exactly the type of A, where-as your specialized template<typename T1, int SIZE> is not an exact match. I believe MSVC is incorrect in its deduction.

Tags:

C++

Templates