2016-08-05 7 views
2

Angenommen, ich eine Matrix-Klasse haben, und ich möchte meine Matrix Objekte auf zwei Arten initialisieren:Wie zwei überladene Funktionen mit Argumenten von nicht verschachtelter und verschachtelter initializer_list unterscheiden?

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

und

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

Als Ergebnis implementiert ich zwei Kopierkonstruktoren wie unten

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

Egal, wie ich meine Schnittstelle ändern, z. B. ein explicit Schlüsselwort hinzufügen oder die verschachtelte Version zu Matrix(initializer_list< vector<double> > matrix) ändern. Es wird immer dazu führen Unklarheiten zwischen diesen beiden Fällen:

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

Ich bin nicht ganz mit dem Zeug wie direkt/Kopie der Initialisierung oder implizite Typumwandlung vertraut. Gibt es Lösungen für dieses Problem?

+0

Versuchen Sie, Ihre Matrixgröße basierend auf der Eingabe anzupassen? Zum Beispiel erwarten Sie, dass '{1, 2, 3}' einen 3x1-Vektor ergibt und '{{1,2,3}, {4,5,6}, {7,8,9}}' ergibt eine 3x3-Matrix? –

+0

@ JonathanMee absolut. – pedim

+0

@pedim Es ist also etwas überraschend, wenn Sie Visual Studio * verwenden, wählt es * den dritten Konstruktor aus. Ich habe eine Frage dazu hier geöffnet: http://stackoverflow.com/q/38795374/2642059 –

Antwort

2

Nun, hier ist ein sehr schmutziger Trick:

#include <iostream> 
#include <initializer_list> 

struct Matrix 
{ 
    template<class = void> Matrix(std::initializer_list<double>) { std::cout << "vector\n"; } 
    Matrix(std::initializer_list<std::initializer_list<double>>) { std::cout << "matrix\n"; } 
}; 

int main() 
{ 
    Matrix a = {1, 2, 3}; 
    Matrix b = {{1}, {2}, {3}}; 
    (void)a; (void)b; 
} 

Die beiden Überlastungen nicht unterschieden auf Conversions basieren, so verlassen wir uns auf einem nachfolgenden Schritt in der Überladungsauflösung Prozess: eine Nicht-Template-Funktion über bevorzugt wird eine Template-Spezialisierung

+1

_Disgraceful! _ Ich liebe es.Ich hatte auch keine Ahnung, dass unbenannte Template-Argumente erlaubt waren, da ich so etwas noch nie verwenden musste ... zum Glück ... _yet_. ;-) –

0

Warum nicht einfach einen Konstruktor erstellen, der eine Matrix annimmt, eine private Funktionskopie erstellt und innerhalb der Kopie nach einem row_vector sucht.

private void Matrix::copy(const Matrix &matrix) 
{ 
    if (matrix.rows == 1) 
    { 
     //row vector stuff here 
    } 
    else if (matrix.cols == 1) 
    { 
     //col vector stuff here 
    } 
    else 
    { 
     //matrix stuff here 
    } 
} 
+0

Aber mein Zweck ist, zwei Arten von Initialisierern zu erhalten. Wie kann ich nur einen Konstruktor verwenden, um mit zwei Arten von Objekten umzugehen? – pedim

+0

Warum initialisieren Sie den Zeilenvektor nicht wie folgt: Matrix a = {{1,2,3}} Entfernt die Notwendigkeit für einen Ihrer Konstruktoren. – Richard

+0

Das OP möchte in der Lage sein, aus Bequemlichkeit zwei verschiedene Arten von Argumenten zu erhalten, nicht verschiedene Dinge mit einem einzigen Typ. Dies unterscheidet nicht zwischen angeforderten Argumenttypen, macht keine Mühe zu erklären, wie diese private Funktion verwendet/nützlich wäre ... und würde nicht einmal kompilieren: Sie haben entweder einen Doppelpunkt (und einen Zeilenumbruch) vergessen, oder Sie ' denke über Java/C# nach. –

4

Es gibt keine Lösung, die in jedem Fall eindeutig funktioniert. Sie können jedoch Möglichkeiten schaffen, um Fälle disambiguate:

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

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

Aber ich persönlich würde vorschlagen, dass Sie es explizit sein. Wenn ein Benutzer eine Matrix, die eine Reihe will, dann sollte es aussehen wie eine Matrix eine Zeile enthält, nicht einen Vektor mag:

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

So würde ich vorschlagen, die erste Überlast insgesamt Notwasserung.

Verwandte Themen