2017-08-03 5 views
1
#include <iostream> 
#include <map> 
using namespace std; 

struct FooStruct 
{ 
    int a; 
    int b; 
}; 

int main() 
{ 
    map<int, FooStruct> fooMap; 
    fooMap.emplace<int, FooStruct>(0, {1, 2}); 
    return 0; 
} 

En termes de prévention des copies temporaires, est-ce que l'utilisation correcte de l'emplace est la suivante? Est-ce le formulaire ci-dessus mieux queComment éviter les copies temporaires lors de l'utilisation de emplace pour ajouter à une carte std :: map?

fooMap.emplace(make_pair<int, FooStruct>(0, {1, 2})); 

Ou sont ces formes équivalentes et les deux éviter de créer une copie temporaire de FooStruct?

+0

La différence est 'make_pair' utilise la sémantique de constructeur de déplacement de' pair', tandis que d'autres méthodes appellent le constructeur du template. – Swift

+0

Qu'en est-il de 'fooMap.emplace (std :: piecewise_construct, std :: forward_as_tuple (0), std :: forward_as_tuple (1, 2));'? – max66

+0

@ max66: Voulez-vous dire que votre forme est meilleure que les deux mentionnés ci-dessus? Si oui, pouvez-vous expliquer pourquoi c'est le cas? – DigitalEye

Répondre

0

EDIT:

des trois formes décrites dans ce fil, celui qui évite des copies inutiles est la forme proposée par @ max66. Le code suivant et sa sortie capte ces trois formes dans l'action

#include <iostream> 
#include <map> 
using namespace std; 

struct FooStruct 
{ 
    FooStruct() 
    { 
     cout << "FooStruct Default Constructor" << endl; 
    } 
    FooStruct(const FooStruct& other) 
    { 
     this->a = other.a; 
     this->b = other.b; 
     cout << "FooStruct Copy Constructor" << endl; 
    } 
    FooStruct(int a, int b) 
    { 
     this->a = a; 
     this->b = b; 
     cout << "FooStruct Parametrized Constructor" << endl; 
    } 
    int a; 
    int b; 
}; 

sortie:

foo.emplace<int, FooStruct>(0, {1, 2}) 
FooStruct Parametrized Constructor 
FooStruct Copy Constructor 
fooMap.emplace(make_pair<int, FooStruct>(1, { 2, 3 })) 
FooStruct Parametrized Constructor 
FooStruct Copy Constructor 
FooStruct Copy Constructor 
fooMap.emplace(std::piecewise_construct, std::forward_as_tuple(2), std::forward_as_tuple(2, 4)) 
FooStruct Parametrized Constructor 

============

ORIGINAL (MAL)

J'étais un peu paresseux et je n'ai pas essayé de creuser plus avant de poster la question. Je vois maintenant que toutes ces trois formes (la troisième forme vient du commentaire de @ max66) sont équivalentes en ce qu'elles évitent toutes trois la création d'une copie temporaire de FooStruct.

#include <iostream> 
#include <map> 
using namespace std; 

struct FooStruct 
{ 
    FooStruct() { cout << "FooStruct Default Constructor" << endl; } 
    FooStruct(int a, int b) { this->a = a; this->b = b; cout << "FooStruct Parametrized Constructor" << endl; } 
    int a; 
    int b; 
}; 

int main() 
{ 
    map<int, FooStruct> fooMap; 
    fooMap.emplace<int, FooStruct>(0, {1, 2}); 
    fooMap.emplace(make_pair<int, FooStruct>(1, { 2, 3 })); 
    fooMap.emplace(std::piecewise_construct, std::forward_as_tuple(2), std::forward_as_tuple(2, 4)); 
    return 0; 
} 

Le code ci-dessus (construite avec Visual C++ 2015) produit la sortie suivante:

FooStruct Parametrized Constructor 
FooStruct Parametrized Constructor 
FooStruct Parametrized Constructor 

PS: je ne vérifie que chaque ligne de la sortie ci-dessus correspond à un seul appel emplace ci-dessus

+1

Je ne suis pas d'accord sur le fait que "que toutes ces trois formes [...] soient équivalentes dans la mesure où toutes les trois évitent la création d'une copie temporaire de FooStruct". Ceci est vrai pour la première et la troisième forme; dans le second cas, je sais qu'il y a la création d'une copie temporaire de la paire 'int' /' FooStruct' qui est utilisée pour construire la paire dans la carte avec le constructeur copy (déplacer le constructeur, à partir de C++ 11). – max66

+0

@ max66: Vous avez raison. J'ai oublié le constructeur de la copie et j'ai évidemment atteint la mauvaise conclusion. Donc, maintenant nous savons que votre formulaire qui implique forward_as_tuple est la meilleure solution. Je vais modifier ma réponse pour refléter cette conclusion. – DigitalEye

1

Si vous définissez la « décision correcte » comme brièveté, vous pouvez utiliser std::map::insert au lieu de std::map::emplace comme ceci:

fooMap.insert({0, {1, 2}}); 

Avec emplace vous devrez soit spécifier les types explicitement comme dans votre exemple ou définir un constructeur en FooStruct explicitement dans le cas suggéré par @ max66:

fooMap.emplace(std::piecewise_construct, 
    std::forward_as_tuple(0), 
    std::forward_as_tuple(1, 2)); 

(qui manque aussi la brièveté).

fooMap.insert({0, {1, 2}}); ne devrait pas être différent de

fooMap.emplace(make_pair<int, FooStruct>(0, {1, 2})); 

en termes de quantité d'objets créés car il utilise également déplacer constructeur de std::pair comme @Swift souligné.

Si "correct" signifie "compilable et fonctionne comme prévu lors de l'exécution", alors vos deux exemples sont corrects.

+0

D'accord, merci. Ce que j'étais réellement après était une seule instance de FooStruct qui est construite directement dans la mémoire de la carte. 'fooMap.emplace (std :: piecewise_construct, std :: forward_as_tuple (0), std :: forward_as_tuple (1, 2));' accomplit juste cela. Les autres formes, concises ou non, impliquent la création d'une instance temporaire de FooStruct, ce que je veux éviter. – DigitalEye

+0

Pourriez-vous s'il vous plaît mettre à jour votre question afin de refléter le fait que vous cherchiez une quantité minimale d'objets construits? En outre, cette forme de 'emplace' peut produire peu ou pas d'avantages de performances d'exécution car les compilateurs peuvent optimiser toute la différence et vous devrez peut-être comparer l'assemblage. –