How do I solve the problem of ambiguity of constructors with arrays and constructors with arrays of arrays in C++?

I want to make my own struct for a 3×3 Matrix. I want to allow construction via components/elements or by "rows".

So either you provide a std::array<float, 9> or a std::array<std::array<float, 3>, 3>

However when defining the struct like this with the following constructors:

struct Matrix3x3
{
    Matrix3x3(std::array<float, 9> components) { }

    Matrix3x3(std::array<std::array<float, 3>, 3> rows) { }
};

Then the second constructor is ambiguos with the first. Meaning that you can call the second constructor like this

Matrix3x3{ {{ {{1.f, 2.f, 3.f}}, {{4.f, 5.f, 6.f}}, {{7.f, 8.f, 9.f}} }} };

without any problems, however calling the first constructor like this

Matrix3x3{ {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f}} };

will give the following message and error:

message : No constructor could take the source type, or constructor overload resolution was ambiguous
error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'ArrayTest'

And Visual Studio tells me that there is "more than one instance of constructor … matches the argument list".

I tested doing the same thing but with an array of ints( because its easier to work with) and a length of one. Consider:

struct ArrayTest
{
    ArrayTest(std::array<int, 1> arrayOfInts) { }

    ArrayTest(std::array<std::array<int, 1>, 1> arrayOfArraysOfInts) { }
};

Then the first 3 are valid and compile for the first constructor, while all 5 of them compile for the second constructor.

auto test1 = ArrayTest{ {1} };

auto test2 = ArrayTest{ { {1} } };

auto test3 = ArrayTest{ { { {1} } } };

auto test4 = ArrayTest{ { { { {1} } } } };

auto test5 = ArrayTest{ { { { { {1} } } } } };

For the simple array constructor "test3" is the full and complete initialization, where the first pair of brackets initializes the aggregate ArrayTest, the second pair initializes the array, the third pair initializes the first element of that array and finally the fourth bracket initializes the integer 1 which is rarely seen, but valid. "test1" and "test2" are then just brace-elisioned versions of "test3".

For the array-of-arrays constructor it is similar where "test5" is the complete initialization and all others are brace-elisioned. This is what causes the ambiguity.

So the question is: How do I solve this problem? Or is there a better way/solution?

Source: Windows Questions C++

LEAVE A COMMENT