Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting between vectors with different allocators

I have written a simple C++11 style stateful allocator type. Given

template<typename T> class my_allocator {
   // the usual stuff
};

template<typename T> using my_vector = std::vector<T, my_allocator<T>>;

my_vector<int> x;

std::vector<int> y = x; // error

What is the best way to allow conversions from a my_vector to a std::vector using the default allocator? GCC 4.7 (recent svn) says

error: conversion from 'my_vector<int> {aka std::vector<int, my_allocator<int>>}' to non-scalar type 'std::vector<int>' requested

Obviously this could be done with, say, a simple conversion function such as

template<typename T> std::vector<T> to_std_vec(const my_vector<T>& v)  {
   return std::vector<T>(&v[0], &v[v.size()]);
   }

but this seems pretty inelegant. Is there a better solution in C++11?

Move semantics are right out in this situation, of course, but I'd like copy construction and assignment to work without extra noise/typing.

like image 923
Jack Lloyd Avatar asked Nov 19 '11 00:11

Jack Lloyd


3 Answers

You cannot possibly hope to get around the obvious copy: std::vector<int> y(x.begin(), x.end();. The elements in y will be allocated by the std::allocator, while the elements in x were allocated, and will be destroyed, by your own allocator. The two allocators could have entirely unrelated notions of memory and pointers! There's no reason that the memory storage that's used by one allocator is in any way related to that of the other.

What else then can you do but make a semantic element copy?

If you don't want the old container any more, you should do a move, though:

std::vector<int> y(std::make_move_iterator(x.begin()),
                   std::make_move_iterator(x.end()));

(This will help in case the elements have their own allocator which agrees. Not for int, of course.)

Update: To reinforce the point, please note that the internal data buffer of a vector<T, Alloc> is not of type T*, but rather of type Alloc::pointer. There's no reason that those are related types for two distinct allocators.

like image 162
Kerrek SB Avatar answered Nov 15 '22 12:11

Kerrek SB


Hmm, I think your only option here is full copy like:

std::vector<int> y( x.begin(), x.end());
like image 20
Nikolai Fetissov Avatar answered Nov 15 '22 13:11

Nikolai Fetissov


There are 3 solutions to your problem:

I. If your vector 'y' does not exist yet, you could use :

std::vector<int> y(x.begin(), x.end());

II. If your vector 'y' already exist, you could use : (e.g. for a member of a class)

this->y.assign(x.begin(), x.end());

These 2 ways to fill a vector are not looking the container type. (and therefore the allocator type)

III. Another solution is to create a parent class which could be used as an allocator adapter between the std allocator and your own allocator. In this case, your 2 allocators are of the same type, and could be used with vector methods like operator=().

like image 2
Axel Borja Avatar answered Nov 15 '22 14:11

Axel Borja