2016-07-26 33 views
2

Ich bin mir nicht sicher, ob dies ein Fehler mit meiner C++ - Syntax ist, oder ob dies überhaupt nicht möglich ist.Möglich std :: map als rvalue zu erstellen?

Ich möchte eine Klasse definieren, die eine Std :: Map als Konstruktorargument verwendet. Ich möchte dann eine Instanz dieser Klasse erstellen, indem ich ein "temporäres" (passenderweise diesen "rvalue" nennen?) Std :: map übergeben. I.e. Ich möchte nicht einen lvalue std :: map erstellen und dann das an den Konstruktor übergeben.

Kann dies erreicht werden? Ich habe folgendes versucht (kommentierten Zeilen Versuch nicht nachgewiesen)

#include <map> 
#include <string> 
#include <iostream> 

class Test 
{ 
    public: 
     Test(std::map<int,char>& rMap) 
     { 
      std::map<int,char>::iterator iter; 

      for (iter = rMap.begin(); iter != rMap.end(); ++iter) 
      { 
       mMap[iter->first] = mMap[iter->second]; 
      } 
     } 
     virtual ~Test(){} 

    protected: 
     std::map<int, char> mMap; 
}; 

int main() 
{ 
    std::cout << "Hello world!" << std::endl; 

    //Test test({1,'a'});     // Compile error. 
    //Test test(std::map<int,char>(1,'a')); // Also compile error. 
    //Test test(std::map<int,char>{1,'a'}); // Yet again compile error. 

    return 0; 
} 

Das ist mein Compiler:

g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) 

Kompilieren Fehler auf Anfrage gebucht werden können, aber ich bin nicht sicher, ob sie nützlich wäre, wenn mein Problem syntaktisch ist.

Vielen Dank.

+0

Haben Sie versucht Test test (std :: map {{1, 'a'}}); 'mit zwei Gruppen von geschweiften Klammern? –

+0

@TimStraubinger - versuchte dies auf Ihren Vorschlag hin, führte aber zu einem weiteren Kompilierungsfehler. – StoneThrow

+0

_What_ "Kompilierfehler"? –

Antwort

1
#include <map> 

class Test { 
public: 
    Test(std::map<int,char> const& cMap) 
    { 
     std::map<int,char>::const_iterator iter; 
     for (iter = cMap.cbegin(); iter != cMap.cend(); ++iter) 
     { 
      mMap[iter->first] = mMap[iter->second]; 
     } 
    } 
    virtual ~Test() { } 
protected: 
    std::map<int, char> mMap; 
}; 

int main() { 
    Test test(std::map<int,char>({{1,'a'}, {2, 'b'}})); 
    return 0; 
} 

Einige Erklärungen:

  1. Sie benötigen Test(T), Test(const T&) oder Test(T&&) Konstruktor, wenn Sie temporäre Objekt (wie std::map<int,char>()) hinein übergeben möchten . Vergessen Sie nicht, wofür T&& Konstruktoren verwendet werden. Im Zweifelsfall benutze sie nicht.
  2. Wenn Sie mit const std::vector/map/list/... arbeiten, können Sie .begin() und .end() nicht verwenden, um Elemente durchlaufen - verwenden. c beginnen() und. c Ende(). Darüber hinaus wird es einfacher mit auto.Versuchen Sie einfach,

    for (auto iter = rMap.cbegin(); iter != rMap.cend(); ++iter) 
    

    statt

    std::map<int,char>::const_iterator iter; 
    for (iter = rMap.cbegin(); iter != rMap.cend(); ++iter) 
    
  3. Um std::map Gebrauch "Doppel-Klammer" initializers wie {{1,'a'}, {2, 'b'}} zu initialisieren - {{key, value}, {second_key, second_value}, ...} diese Konstruktion verfügbar ist, weil

    1) std::map diesen Konstruktor hat:

    map(std::initializer_list<value_type> init, 
        const Compare& comp = Compare(), 
        const Allocator& alloc = Allocator()); 
    // see http://en.cppreference.com/w/cpp/container/map/map 
    

    2) innere Klammern {1,'a'} wird als value_type Konstruktoraufruf interpretiert. Und value_type für std::map<int, char> ist std::pair<int, char>.

P. S. vergessen Sie nicht, dass std::map<int,char>() und std::map<int,char>{} Konstruktoraufrufe gleich sind. Das heißt, Sie vorderen Halterungen fallen können: std::map<int,char>({{1,'a'},{2,'b'}}) --> std::map<int,char>{{1,'a'},{2,'b'}}

+2

Ich mag, wie deine Prosa und dein Beispiel fast polare gegensätzliche Dinge sagen. –

+0

@LightnessRacesinOrbit Nun wollte der Fragesteller den r-Wert im Konstruktor akzeptieren. Mein Beispiel löst die Frage des Autors, aber ich frage mich immer noch, ob der Autor weiß, wo r-Werte verwendet werden sollen. – slavanap

+1

Würdest du sagen, dass diese Antwort dieses Verständnis erhöhen wird? –

4

Ja, aber Ihr Konstruktor verwendet eine lvalue-Referenz. Es muss stattdessen eine Referenz zu const oder eine R-Referenz sein.

Genau wie bei jedem anderen Typ.

+0

Wenn ich einen Verweis auf const (im Gegensatz zu der Rvalue-Referenz) verwenden möchte, was ist die korrekte Syntax? Nachdem ich den Konstruktor Test (std :: map const & rMap) definiert habe, habe ich immer noch Schwierigkeiten damit, dies mit einer rvalue std :: map-Instanz aufzurufen. – StoneThrow

+0

@StoneThrow: Was bedeutet "ich kämpfe" _actually_? –

+0

"Kämpfen" bedeutet hier insbesondere fortgesetzte Kompilierungsfehler. Nachdem ich den Konstruktor in Test geändert habe (std :: map const & rMap), kann ich immer noch kein Test-Objekt mit einer der drei Methoden in meinem main() instanziieren. Was ist die korrekte Syntax für _instantiate_ ein Test-Objekt, wenn sein Konstruktor einen Verweis auf const hat? – StoneThrow

5

Test(std::map<int, char> rMap) : mMap(std::move(rMap)) {} 

oder

Sie
Test(std::map<int, char>&& rMap) : mMap(std::move(rMap)) {} 

oder

Test(const std::map<int, char>& rMap) : mMap(rMap) {} 

vorübergehend nicht const l-Referenzwert nicht binden kann.

Und verwenden Sie es als

Test test({{1,'a'}}); 
Test test2({{1,'a'}, {2, 'b'}}); 
+3

Okay. _Warum? _ Nicht nützlich ohne Erklärung. Nicht nur Code-Dump. –

+0

@ Jarod42 Wenn ich mit Option 1 gehe, was ist die korrekte Methode, ein Testobjekt zu instanziieren? Ich habe immer noch Probleme mit der Syntax zum Erstellen eines Rvalue std :: map. – StoneThrow

0

Sie verwenden sollten:

class Test 
{ 
public: 
    Test(const std::map<int,char>& rMap) : mMap(rMap) 
    {} 
    Test(std::map<int,char>&& rMap) : mMap(std::move(rMap)) 
    {} 
    virtual ~Test(){} 
protected: 
    std::map<int, char> mMap; 
}; 

Dies bietet zwei Konstruktoren: eine, die nimmt einen const lvalue Bezug und kopiert den Inhalt in mMap und eine, die einen R-Wert Bezug nimmt und verschiebt seinen Inhalt in mMap. Auf diese Weise können Sie eine Test Instanz konstruieren, indem entweder eine benannte std::map oder eine vorübergehende:

int main() 
{ 
    std::map<int,char> m{{1, 'a'}, {2,'b'}}; 
    Test t1{m}; 
    Test t2{{{1, 'a'}, {2, 'b'}}}; 
} 

Auf diese Weise können Sie unnötige Kopien zu vermeiden, wenn Sie können, aber noch können Sie eine Kopie des map machen, wenn Sie benötigen.

+0

Sicher ist der Weg, dies zu tun, eine einzige Überladung, die die Karte nach Wert nimmt? –

+0

@LightnessRacesinOrbit Leute (z. B. Niebler) sagen das, aber es wäre toll, wenn das keinen zusätzlichen Zug nach sich ziehen würde. – Barry

+0

@Barry: Eine Bewegung ist effektiv frei, auch wenn sie nicht weg optimiert werden kann (was selten ist). Viel besser als sich selbst zu wiederholen, es sei denn, Sie haben einen Umstand, unter dem dies wirklich ein Problem ist. –