VStudio 2012 Создание пользовательского распределителя для контейнера типа "только для перемещения"

Я пытаюсь создать контейнер stl типа только для перемещения, который использует собственный распределитель в VStudio 2012.

Проблема в том, что мне кажется, что я должен предоставить функцию построения для распределителя, который, в свою очередь, нуждается в доступе к общедоступному конструктору копии содержащегося типа.

Я либо получаю:

ошибка C2248: 'std::unique_ptr‹_Ty>::unique_ptr': невозможно получить доступ к частному члену, объявленному в классе 'std::unique_ptr‹_Ty>'

or

ошибка C2039: «конструкция»: не является членом «MyAllocator»

Тот же код работает в clang, поэтому я подозреваю, что проблема связана с Microsoft, но может ли кто-нибудь предложить возможный обходной путь?

Это мой код для минимального воспроизведения

#include <memory>
#include <vector>

using namespace std;

template< typename T>
struct MyAllocator
{
typedef T value_type;
typedef value_type*         pointer;
typedef value_type&         reference;
typedef const value_type*   const_pointer;
typedef const value_type&   const_reference;
typedef size_t              size_type;
typedef ptrdiff_t           difference_type;


template<class t_other>
struct rebind
{
    typedef MyAllocator<t_other> other;
};

MyAllocator():m_id(0) {}
MyAllocator(int id):m_id(id){}

template <class T> 
MyAllocator(const MyAllocator<T>& other)
    :m_id(other.getId())
{

}  

T* allocate(std::size_t n)
{
    return reinterpret_cast<T*>(malloc(sizeof(T) * n));
}

void deallocate(T* p, std::size_t n)
{
    free(p);
}

int getId() const{ return m_id;}


//Have to add these although should not be necessary
void construct(pointer mem, const_reference value)
{
    std::_Construct(mem, value);      
}

void destroy(pointer mem)
{
    std::_Destroy(mem);
}

private:
    int m_id;
};

template <class T1, class U>
bool operator==(const MyAllocator<T1>& lhs, const MyAllocator<U>& rhs)
{
    return lhs.getId() == rhs.getId() ;
}

template <class T1, class U>
bool operator!=(const MyAllocator<T1>&, const MyAllocator<U>&)
{
    return lhs.getId()  != rhs.getId();
}

//define a move only type
typedef unique_ptr<uint32_t> MyIntPtr;

//define a container based on MyIntPtr and MyAllocator
typedef vector<MyIntPtr, MyAllocator<MyIntPtr> > MyVector;

int main(int argc, char* argv[])
{
   MyAllocator<MyIntPtr> alloc1(1);

   MyVector vec(alloc1);

   uint32_t* rawPtr = new uint32_t;
   *rawPtr = 18;

   vec.emplace_back(rawPtr);
   return 0;
}

person David Woo    schedule 26.08.2015    source источник
comment
С какой версией clang вы это скомпилировали? gcc.godbolt.org, кажется, показывает разные результаты - компиляция с clang также не удалась.   -  person Rudolfs Bundulis    schedule 26.08.2015
comment
Пусть construct возьмет T&& в качестве второго параметра и перейдет от него. Более того, в C++11 предполагается, что construct принимает произвольный набор аргументов и выполняет эквивалент new(p) T(std::forward(args)) (не уверен, что VC12 достаточно хорошо поддерживает C++11, чтобы разрешить это). Это необходимо для поддержки emplace и подобных.   -  person Igor Tandetnik    schedule 26.08.2015


Ответы (1)


Ошибка, которую вы получаете, связана с тем, что вы пытаетесь создать std::unique_ptr. из постоянной ссылки на std::unique_ptr того же типа - и такого конструктора нет.

Вы можете переработать свой метод construct, чтобы получить ссылку на rvalue, и тогда все будет хорошо компилироваться:

void construct(pointer mem, value_type&& value)
{
    std::_Construct(mem, std::move(value));      
}
person Rudolfs Bundulis    schedule 26.08.2015
comment
не должно ли это быть std::_Construct(mem, std::move(value)); ? - person David Woo; 26.08.2015
comment
или шаблон‹ имя_типа T3 › недействительная конструкция(память указателя, T3&& значение) { std::_Construct(mem, std::forward‹T3›(значение)); } Кажется, оба работают - person David Woo; 26.08.2015
comment
На самом деле движение против вперед здесь заставило меня немного задуматься - я редактировал ответ 3 раза: D Ну, насколько я понимаю, вперед кажется более подходящим, так как вы хотите переслать значение как есть после разрушения ссылки, но я могу ошибаться, и если кто-то демонстрирует, насколько перемещение лучше в этом случае, я с радостью отредактирую :) Я все еще не так силен в C++ 11, но, насколько я понимаю, вперед здесь кажется более подходящим. - person Rudolfs Bundulis; 26.08.2015
comment
Я думал, что у std::forward есть только один вариант использования, и он должен был пересылать универсальную ссылку, которую вы получили с помощью шаблона. stackoverflow.com/questions/9671749 / - person David Woo; 26.08.2015
comment
@DavidWoo, может быть, я ошибаюсь, но, поскольку тип значения может иметь разные конструкторы, не должна ли в этом случае требоваться идеальная переадресация? Опять же, не уверен на 100%. - person Rudolfs Bundulis; 26.08.2015
comment
@DavidWoo хорошо, но поскольку вам нужен тип только для перемещения, вы на самом деле хотите избежать любых ссылок на lvalue, поэтому я думаю, что мои первоначальные рассуждения были неправильными, и перемещение более уместно :) Спасибо за вопрос, это на самом деле заставило меня узнать немного больше . - person Rudolfs Bundulis; 26.08.2015
comment
Вы правы в том, что следует использовать идеальную переадресацию, и общий случай заключается в том, что конструкция должна быть методом шаблона с переменным числом аргументов, начиная с С++ 11 (который VS2012 не поддерживает). Ваш код в настоящее время просто принимает ссылку rvalue и поэтому должен использовать move. - person David Woo; 26.08.2015