Search code examples
c++overloadingambiguousstdarray

C++ std::array as a parameter of overloaded function is ambiguous


I was expecting the compiler to choose which function to use, depending on the size of the array. It works properly on call funct({1,2,3});, but the others are ambiguous, why? Isn't array<int,1> a different data type from array<int,2>, array<int,3>, and so on?

Here is my code:

#include<iostream>
#include<array>
using namespace std;
void funct(array<int,1>one)
{
    cout<<"one"<<endl;
}
void funct(array<int,2>two)
{
    cout<<"two"<<endl;
}
void funct(array<int,3>three)
{
    cout<<"three"<<endl;
}
int main()
{
    funct({1,2,3});
    funct({1,2});
    funct({1});
    return(0);
}

Here are my build messages ([redacted] is what i removed for obvious reasons):

||=== Build: Debug in ambiguitytest (compiler: GNU GCC Compiler) ===|
[redacted]\ambiguitytest\main.cpp||In function 'int main()':|
[redacted]\ambiguitytest\main.cpp|19|error: call of overloaded 'funct(<brace-enclosed initializer list>)' is ambiguous|
[redacted]\ambiguitytest\main.cpp|8|note: candidate: void funct(std::array<int, 2u>)|
[redacted]\ambiguitytest\main.cpp|12|note: candidate: void funct(std::array<int, 3u>)|
[redacted]\ambiguitytest\main.cpp|20|error: call of overloaded 'funct(<brace-enclosed initializer list>)' is ambiguous|
[redacted]\ambiguitytest\main.cpp|4|note: candidate: void funct(std::array<int, 1u>)|
[redacted]\ambiguitytest\main.cpp|8|note: candidate: void funct(std::array<int, 2u>)|
[redacted]\ambiguitytest\main.cpp|12|note: candidate: void funct(std::array<int, 3u>)|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Solution

  • An std::array<int, N> can be initialized with a braced initializer list of any length up to N. It performs aggregate initialization and initializes the remaining elements of the array to zero.

    So for both funct({1,2}); and funct({1}); multiple overload candidates are viable.

    There is no rule that makes an aggregate initialization with more matching elements a better match in overload resolution, so overload resolution is ambiguous.

    If you need to determine the length of the initializer list, you can use a template instead:

    template<std::size_t N>
    void funct(const int (&arr)[N])
    {
        if(N == 1)
            cout<<"one"<<endl;
        else if(N == 2)
            cout<<"two"<<endl;
        else if(N == 3)
            cout<<"one"<<endl;
        else
            cout<<"something else"<<endl;
    }
    

    (Note that this only works with references to built-in arrays as function parameter. It doesn't work with std::array. Built-in arrays have special deduction rules that allow this.)