Je suis intéressé par la construction d'un Je voudrais construire mon conteneur dans la même veine que existe un moyen simple de contrôler par défaut Construction de "extérieur" de Voici la solution que je suis arrivée à, qui a inspiré la réponse de Kerrek: P> Uninitialized_vector Code>, qui sera sémantiquement identique à
std :: vecteur code> avec la mise en garde que les nouveaux éléments seraient autrement créés avec un Le constructeur de non-arguments sera plutôt créé sans initialisation. Je suis principalement intéressé à éviter l'initialisation de la cossane à 0.
autant que je puisse dire, il n'y a aucun moyen de l'accomplir en combinant std :: vecteur code> avec un type particulier d'allocateur. Strike>
std :: pile code>, qui adapte un conteneur fourni par l'utilisateur (dans mon cas,
std :: vecteur ). En d'autres termes, je voudrais éviter de réimplémenter l'intégralité de
std :: vecteur code> et fournir une "façade" autour de lui. P>
std :: vecteur code>? p>
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <cassert>
// uninitialized_allocator adapts a given base allocator
// uninitialized_allocator's behavior is equivalent to the base
// except for its no-argument construct function, which is a no-op
template<typename T, typename BaseAllocator = std::allocator<T>>
struct uninitialized_allocator
: BaseAllocator::template rebind<T>::other
{
typedef typename BaseAllocator::template rebind<T>::other super_t;
template<typename U>
struct rebind
{
typedef uninitialized_allocator<U, BaseAllocator> other;
};
// XXX for testing purposes
typename super_t::pointer allocate(typename super_t::size_type n)
{
auto result = super_t::allocate(n);
// fill result with 13 so we can check afterwards that
// the result was not default-constructed
std::fill(result, result + n, 13);
return result;
}
// catch default-construction
void construct(T *p)
{
// no-op
}
// forward everything else with at least one argument to the base
template<typename Arg1, typename... Args>
void construct(T* p, Arg1 &&arg1, Args&&... args)
{
super_t::construct(p, std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
};
namespace std
{
// XXX specialize allocator_traits
// this shouldn't be necessary, but clang++ 2.7 + libc++ has trouble
// recognizing that uninitialized_allocator<T> has a well-formed
// construct function
template<typename T>
struct allocator_traits<uninitialized_allocator<T> >
: std::allocator_traits<std::allocator<T>>
{
typedef uninitialized_allocator<T> allocator_type;
// for testing purposes, forward allocate through
static typename allocator_type::pointer allocate(allocator_type &a, typename allocator_type::size_type n)
{
return a.allocate(n);
}
template<typename... Args>
static void construct(allocator_type &a, T* ptr, Args&&... args)
{
a.construct(ptr, std::forward<Args>(args)...);
};
};
}
// uninitialized_vector is implemented by adapting an allocator and
// inheriting from std::vector
// a template alias would be another possiblity
// XXX does not compile with clang++ 2.9
//template<typename T, typename BaseAllocator>
//using uninitialized_vector = std::vector<T, uninitialized_allocator<T,BaseAllocator>>;
template<typename T, typename BaseAllocator = std::allocator<T>>
struct uninitialized_vector
: std::vector<T, uninitialized_allocator<T,BaseAllocator>>
{};
int main()
{
uninitialized_vector<int> vec;
vec.resize(10);
// everything should be 13
assert(std::count(vec.begin(), vec.end(), 13) == vec.size());
// copy construction should be preserved
vec.push_back(7);
assert(7 == vec.back());
return 0;
}
3 Réponses :
Je ne crois pas que cela soit possible en emballant un vecteur (qui fonctionne avec chaque type), sauf si vous redimensionnez le vecteur sur chaque opération ADD et Supprimer EM>. P>. P>.
Si vous pouviez abandonner les conteneurs STL d'emballage, vous pouvez le faire en gardant une matrice de char code> sur le tas et à l'aide de placement
Nouveau code> pour chacun des objets que vous voulez construire. De cette façon, vous pouvez contrôler exactement quand les constructeurs et les destructeurs d'objets ont été appelés, un par un. P>
Au lieu d'utiliser une enveloppe autour du conteneur, envisagez d'utiliser une enveloppe autour du type d'élément:
template <typename T> struct uninitialized { uninitialized() { } T value; };
Ce n'est-ce pas que la valeur de la valeur code>?
Ah, n'a pas réalisé qu'il n'utilisait que des types de pod.
Je pense que quelque chose d'intelligent comme cela peut fonctionner, mais j'ai les exigences supplémentaires des traits de conservation comme std_type code>,
std :: vecteur :: référence code>, etc . Ceux-ci ne devraient pas exposer
ininténalisé
Écrire un Uninitialized_vector
vecteur
Je pense que le problème se résume au type d'initialisation que le conteneur effectue des éléments. Comparaison:
template <typename T> void definit_construct(void * addr) { new (addr) T; // default-initialization }
Merci pour cette réponse, mais je ne comprends pas votre commentaire sur construction () code> prendre un argument qui devient initialisé de la valeur. Pourquoi ne peut-il pas être un non-op?
@Jared: Je veux dire que la façon dont les allocateurs standard sont utilisés dans des conteneurs standard - leur fonction construction () code> est généralement quelque chose comme
construction (vide *, const t &) code>, Donc, il y a au moins une copie pendant la construction, mais j'imagine que cette fonction serait appelée d'un objet initialisé de valeur, comme
construction (p, t ()) code>, vous ne pouvez donc pas échapper à la valeur. initialisation à l'intérieur des conteneurs standard. (Même les allocators Emplacement C ++ 11 ne peuvent pas vous aider car vous ne pouvez pas "déplacer" quelque chose de plus d'une fois ...)
Donc, fondamentalement, je dis que vous pouvez gratiser un conteneur qui se comporte beaucoup comme vecteur code>, mais au lieu d'appeler l'allocator standard, vous devez simplement dire
nouveau (addr) t ; code> au lieu d'appeler
construction () code>. Vous pouvez conserver les autres aspects de l'allocator (c'est-à-dire une allocation de mémoire).
Intéressant - il ressemble à votre redimensionnement (taille_t, t = t ()) code> exemple n'est en réalité pas standard.
std :: vecteur code> est requis pour distinguer entre
redimensionner (Taille_t) code> et
redimensionner (taille_t, const t &) code> (De même pour ses constructeurs). Il semble donc qu'un allocator standard puisse fonctionner, en fonction de la qualité de référence
std :: vecteur code> conforme.
Par "Standard" Je voulais dire bien sûr pris d'une source fiable :-) En effet, N3290 indique que deux signatures distinctes redimatures (taille_t) code> et
redimensionnement (taille_t, const t & t &) code> existe. Il est donc possible que
redimensionner (n) code> se comporte déjà comme vous le souhaitez.
Le problème est qu'il y a Trois types d'initialisation , mais STD :: Allocator Code> n'a que deux: zéro et valeur. Il n'y a pas de méthode d'initialisation par défaut. S'il y en avait, les conteneurs pouvaient éventuellement l'utiliser à la place.
Pouvez-vous simplement définir un constructeur par défaut qui ne fait rien? Je parie que le compilateur peut en ligne ou omettez-le ESP avec optimisation allumé.
@Doug T. - qui peut résoudre le cas du constructeur par défaut, mais les éléments intéressants semble se produire dans
redimensionner code> - il est difficile à appeler des fonctions telles que
redimensionner code> sans invoquer constructeurs.
@Doug: Cela a la mise en garde que vous n'êtes plus pod avec un constructeur défini par l'utilisateur ...