2016-07-26 3 views
2

Je ne suis pas sûr si c'est une erreur avec ma syntaxe C++, ou si c'est quelque chose qui ne peut pas être accompli du tout.Possibilité de créer std :: map comme valeur?

Je veux définir une classe qui prend un std :: map comme argument constructeur. Je veux ensuite créer une instance de cette classe en passant un "temporaire" (approprié d'appeler cette "rvalue"?) Std :: map. C'est à dire. Je ne veux pas créer une lvalue std :: map et ensuite passer cela au constructeur.

Est-ce que cela peut être accompli? J'ai essayé les éléments suivants (lignes commentées montrent tentatives échouées)

#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; 
} 

Ceci est mon compilateur:

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

des erreurs de compilation peuvent être affichées sur demande, mais je ne sais pas si elles seraient utiles si mon problème est syntaxique.

Merci.

+0

Avez-vous essayé 'Test test (std :: map {{1, 'a'}})' avec deux jeux de parenthèses? –

+0

@TimStraubinger - essayé ceci sur votre suggestion, mais a abouti à une autre erreur de compilation. – StoneThrow

+0

_What_ "erreur de compilation"? –

Répondre

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; 
} 

Quelques explications:

  1. Vous avez besoin Test(T), Test(const T&) ou constructeur Test(T&&) si vous voulez passer objet temporaire (comme std::map<int,char>()) dans ce . Ne pas oublier à quoi servent les constructeurs T&&. En cas de doute, ne les utilisez pas.
  2. Si vous travaillez avec const std::vector/map/list/... vous ne pouvez pas utiliser .begin() et .end() pour parcourir les éléments - utiliser. c begin() et. c fin(). En outre, il deviendra plus facile avec auto.Juste essayer

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

    au lieu

    std::map<int,char>::const_iterator iter; 
    for (iter = rMap.cbegin(); iter != rMap.cend(); ++iter) 
    
  3. Pour initialiser comme {{1,'a'}, {2, 'b'}}std::map utilisation initialisations "double du support" - {{key, value}, {second_key, second_value}, ...} cette construction est disponible parce

    1) std::map a ce constructeur:

    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) les accolades intérieures {1,'a'} est interprété comme value_type appel constructeur. Et value_type pour std::map<int, char> est std::pair<int, char>.

P. S. ne faut pas oublier que std::map<int,char>() et std::map<int,char>{} appels constructeur sont égaux. Cela signifie que vous pouvez déposer les supports avant: std::map<int,char>({{1,'a'},{2,'b'}}) --> std::map<int,char>{{1,'a'},{2,'b'}}

+2

J'aime la façon dont votre prose et votre exemple disent presque des choses opposées. –

+0

@ LightnessRacesinOrbit bien, l'auteur de la question voulait accepter r-value dans le constructeur. Mon exemple résout la question de l'auteur, mais je me demande toujours si l'auteur comprend où les valeurs r devaient être utilisées. – slavanap

+1

Diriez-vous que cette réponse est susceptible d'augmenter cette compréhension? –

4

Oui, mais votre constructeur prend une référence lvalue. Il doit plutôt s'agir d'une référence à const ou d'une référence rvalue.

Tout comme avec tout autre type.

+0

Si je veux utiliser une référence à const (par opposition à la référence rvalue), quelle est la syntaxe correcte? Après avoir défini le constructeur Test (std :: map const & rMap), j'ai toujours du mal à invoquer cela avec une instance de rvalue std :: map. – StoneThrow

+0

@StoneThrow: Que signifie "je me bats" _actually_ mean? –

+0

"luttant" ici signifie spécifiquement continuer les erreurs de compilation. Après avoir changé le constructeur en Test (std :: map const & rMap), je ne suis toujours pas capable d'instancier un objet Test en utilisant l'une des trois méthodes de mon main(). Quelle est la syntaxe correcte pour _instantiate_ un objet Test si son constructeur a pris une référence-à-const? – StoneThrow

5

Do

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

ou

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

ou

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

temporaire ne peut se lier à la référence l-valeur non const.

et l'utiliser comme

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

D'accord. _Pourquoi? _ Pas utile sans explication. Ne faites pas que dump de code. –

+0

@ Jarod42 Si je suis allé avec l'option 1, quelle est la bonne façon d'instancier un objet Test? J'ai toujours des problèmes avec la syntaxe de création d'un rvalue std :: map. – StoneThrow

0

Vous devez utiliser:

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; 
}; 

Cela fournit deux constructeurs: celui qui prend const référence lvalue et copie son contenu dans mMap et qui prend une référence rvalue et déplace son contenu en mMap. Cela vous permet de construire une instance Test utilisant un nom std::map ou temporaire:

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

Cela vous permet d'éviter des copies inutiles quand vous pouvez, mais vous permet de faire une copie du map lorsque vous avez besoin.

+0

Sûrement le moyen de le faire est une surcharge unique qui prend la carte en valeur? –

+0

@ LightnessRacesinOrbit Les gens (par exemple Niebler) disent cela, mais ce serait génial si cela n'entraînait pas de mouvement supplémentaire. – Barry

+0

@Barry: Un mouvement est effectivement gratuit, même s'il ne peut pas être optimisé (ce qui est rare). Bien mieux que de vous répéter à moins que vous ayez des circonstances dans lesquelles ceci est vraiment un problème. –