pedim pedim - 3 months ago 10
C++ Question

How to distinguish two overloaded functions with arguments of non-nested and nested initializer_list?

Suppose I have a Matrix class and I'd like to initialize my Matrix objects in two ways:

Matrix a = {1,2,3} // for a row vector


and

Matrix b = {{1,2,3},{4,5,6},{7,8,9}} // for a matrix


As a result, I implemented two copy constructors as below

class Matrix {
private:
size_t rows, cols;
double* mat;
public:
Matrix() {}
Matrix(initializer_list<double> row_vector) { ... }
Matrix(initializer_list< initializer_list<double> > matrix) { ... }
...
}


No matter how I change my interface, such as adding an
explicit
keyword or change the nested version to
Matrix(initializer_list< vector<double> > matrix)
. It will always cause ambiguities between these two cases:

Matrix a = {1,2,3};n
Matrix b = {{1}, {2}, {3}};


I'm not quite familiar with the stuff like direct/copy initialization or implicit type conversion. Are there any solutions for this problem?

Answer

There is no solution which will unambiguously work in every case. However, you can create ways to disambiguate cases:

template<typename T>
auto il(std::initializer_list<T> the_il) -> std::initializer_list<T> { return the_il; }

Matrix b = {il({1}), {2}, {3}};

However, I would personally suggest that you be explicit about it. If a user wants a matrix containing one row, then it should look like a matrix containing one row, not like a vector:

Matrix a = {{1,2,3}};

So I would suggest ditching the first overload altogether.

Comments