114 votes

C++11 emplace_back sur vector<struct> ?

Considérons le programme suivant :

#include <string>
#include <vector>

using namespace std;

struct T
{
    int a;
    double b;
    string c;
};

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

Ça ne marche pas :

$ g++ -std=gnu++11 ./test.cpp
In file included from /usr/include/c++/4.7/x86_64-linux-gnu/bits/c++allocator.h:34:0,
                 from /usr/include/c++/4.7/bits/allocator.h:48,
                 from /usr/include/c++/4.7/string:43,
                 from ./test.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = T; _Args = {int, double, const char (&)[4]}; _Tp = T]’:
/usr/include/c++/4.7/bits/alloc_traits.h:253:4:   required from ‘static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]’
/usr/include/c++/4.7/bits/alloc_traits.h:390:4:   required from ‘static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>]’
/usr/include/c++/4.7/bits/vector.tcc:97:6:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, double, const char (&)[4]}; _Tp = T; _Alloc = std::allocator<T>]’
./test.cpp:17:32:   required from here
/usr/include/c++/4.7/ext/new_allocator.h:110:4: error: no matching function for call to ‘T::T(int, double, const char [4])’
/usr/include/c++/4.7/ext/new_allocator.h:110:4: note: candidates are:
./test.cpp:6:8: note: T::T()
./test.cpp:6:8: note:   candidate expects 0 arguments, 3 provided
./test.cpp:6:8: note: T::T(const T&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided
./test.cpp:6:8: note: T::T(T&&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided

Quelle est la bonne façon de procéder et pourquoi ?

(J'ai aussi essayé les accolades simples et doubles)

6voto

Mark B Points 60200

Cela semble être couvert par l'article 23.2.1/13.

Tout d'abord, les définitions :

Étant donné un type de conteneur X ayant un allocator_type identique à A et un value_type identique à T et étant donné une lvalue m de type A, un pointeur p de type T*, une expression v de type T, et une valeur rv de type T, les termes suivants sont définis. les termes suivants sont définis.

Maintenant, ce qui le rend constructible :

T est EmplaceConstructible en X à partir d'args , pour zéro ou plus arguments args, signifie que l'expression suivante est bien formée : allocator_traits::construct(m, p, args) ;

Et enfin, une remarque sur l'implémentation par défaut de l'appel à la construction :

Note : Un conteneur appelle allocator_traits::construct(m, p, args) pour construire un élément à p en utilisant les arguments. La construction par défaut dans allocateur appelle ::new((void*)p) T(args), mais les allocateurs spécialisés allocateurs spécialisés peuvent choisir une définition différente.

Cela nous dit à peu près que pour un schéma d'allocateur par défaut (et potentiellement le seul) il faut doit avoir défini un constructeur avec le nombre approprié d'arguments pour la chose que vous essayez d'insérer dans un conteneur.

-2voto

zaufi Points 1837

Vous devez définir un constructeur pour votre type T car il contient un std::string ce qui n'est pas trivial.

de plus, il serait préférable de définir un ctor/assign (éventuellement par défaut) de déplacement (parce que vous avez un movable std::string comme membre) - cela aiderait à déplacer votre T beaucoup plus efficace...

ou, utilisez simplement T{...} pour appeler les surcharges emplace_back() comme recommandé dans la réponse de neighboug... tout dépend de vos cas d'utilisation typiques...

-3voto

AlexB Points 1

Vous pouvez créer le struct T et ensuite le déplacer vers le vecteur :

V.push_back(std::move(T {42, 3.14, "foo"}));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X