495 / 209 / 70
Регистрация: 27.05.2016
Сообщений: 557
1

Allocator_traits propagate_on_container_ - для чего они нужны

26.06.2016, 21:59. Показов 4655. Ответов 4
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
В std::allocator_traits есть такие определения как propagate_on_container_copy_assignment, propagate_on_container_move_assignment и propagate_on_container_swap. Для чего они нужны и как ими можно пользоваться? С названия, я так понимаю, они должны определять копируемость, перемещаемость и обмениваемость своего типа, но вот код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
    std::allocator<std::string> str_allock;
    std::allocator<int> int_allock;
 
    std::cout << std::boolalpha;
    std::cout << std::allocator_traits<decltype(str_allock)>::propagate_on_container_swap::value << "\n"; //false
    std::cout << std::allocator_traits<decltype(str_allock)>::propagate_on_container_move_assignment::value << "\n"; //true
    std::cout << std::allocator_traits<decltype(str_allock)>::propagate_on_container_copy_assignment::value << "\n"; //false
 
    std::cout << std::allocator_traits<decltype(int_allock)>::propagate_on_container_swap::value << "\n"; //false
    std::cout << std::allocator_traits<decltype(int_allock)>::propagate_on_container_move_assignment::value << "\n"; //true
    std::cout << std::allocator_traits<decltype(int_allock)>::propagate_on_container_copy_assignment::value << "\n"; //false
}
Почему то для swap и copy_assignment false определяет.

Добавлено через 57 секунд
Да, и еще одно, что такое is_always_equal (since C++17) ?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
26.06.2016, 21:59
Ответы с готовыми решениями:

Для чего нужны сужающие преобразования как они работают и на сколько они важны?
Я читаю одну книгу и застрял на одной теме &quot;Преобразования&quot;, и там есть такой вот код double х...

Перечисления (enum): для чего они нужны? Как, когда и какими перечислениями уместнее пользоваться?
Предположим есть такой код: #include &lt;iostream&gt; #include &lt;iomanip&gt; #include &lt;Windows.h&gt; enum...

Объясните 35-36 строчки ,что они делают и для чего они ?
//--------------------------------------------------------------------------- #include&lt;iostream.h&gt;...

.elf и для чего они нужны
Собственно сабж Почитал Вики, как-то для новичка не очень много прояснилось И объясните еще...

4
Игогошка!
1801 / 708 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
27.06.2016, 00:06 2
Лучший ответ Сообщение было отмечено notAll как решение

Решение

Цитата Сообщение от notAll Посмотреть сообщение
Для чего они нужны и как ими можно пользоваться?
Ну вот у контейнера есть аллокатор. У нас есть один объект - cont1, другой - cont2. Мы делаем:
cont1 = cont2;
cont1 = std::move(cont2);
std::swap(cont1, cont2);
Что делать с элементами контейнера понятно и ежу, а вот что с аллокаторами? Должен ли cont1 продолжать использовать свой старый аллокатор, или должен брать из cont2?
Что мы будем делать с аллокаторами в каждом конкретном случае - и определяется этими свойствами propagate_on_container_ххх.
И я бы еще обратил тогда уж внимание на пару select_on_container_ххх.

Цитата Сообщение от notAll Посмотреть сообщение
Да, и еще одно, что такое is_always_equal (since C++17) ?
У нас есть класс аллокатора Alloc. Два его объекта-аллокатора a1 и a2 равны, если то, что выделено а1, может быть освобождено а2, - и наоборот.
Если а1 и а2 для нашего Alloc всегда равны, то мы ставим is_always_equal в true_type. Если всегда не равны - false_type.
По умолчанию же, а1 и а2 равны, только если у них нет внутреннего состояния (is_empty).
1
495 / 209 / 70
Регистрация: 27.05.2016
Сообщений: 557
27.06.2016, 00:44  [ТС] 3
Цитата Сообщение от ct0r Посмотреть сообщение
Что мы будем делать с аллокаторами в каждом конкретном случае - и определяется этими свойствами propagate_on_container_ххх.
Все равно не очень понятно как задать эти свойства самому?
Цитата Сообщение от ct0r Посмотреть сообщение
И я бы еще обратил тогда уж внимание на пару select_on_container_ххх.
Я вижу только одну статическую функцию select_on_container_copy_construction()

Вот тут у меня есть код небольшего самописного контейнера. Как мне эти свойства задать здесь?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include <iostream>
#include <iterator>
#include <type_traits>
#include <utility>
#include <memory>
#include <initializer_list>
#include <stdexcept>
 
template <typename T, typename Allocator = std::allocator<T> >
class Vec {
public:
    // types:
    typedef T                                                                           value_type;
    typedef Allocator                                                                   allocator_type;
    typedef value_type&                                                                 reference;
    typedef const value_type&                                                           const_reference;
    typedef typename std::iterator<std::random_access_iterator_tag, T>::pointer         iterator;
    typedef typename std::iterator<std::random_access_iterator_tag, const T>::pointer   const_iterator;
    typedef typename std::allocator_traits<Allocator>::size_type                        size_type;
    typedef typename std::allocator_traits<Allocator>::difference_type                  difference_type;
    typedef typename std::allocator_traits<Allocator>::pointer                          pointer;
    typedef typename std::allocator_traits<Allocator>::const_pointer                    const_pointer;
    typedef std::reverse_iterator<iterator>                                             reverse_iterator;
    typedef std::reverse_iterator<const_iterator>                                       const_reverse_iterator;
 
    // construct/copy/destroy:
    explicit Vec(const Allocator& alloc) noexcept(noexcept(Allocator())) : alloc(alloc) {}
    Vec() noexcept(noexcept(Allocator())) : Vec(Allocator()) {}
 
    explicit Vec(size_type count, const Allocator& alloc = Allocator()) : alloc(alloc)
    {
        auto data = alloc_n_fill(count, value_type());
        elements = data.first;
        first_free = cap = data.second;
    }
 
    Vec(size_type count, const T& value, const Allocator& alloc = Allocator()) : alloc(alloc)
    {
        auto data = alloc_n_fill(count, value);
        elements = data.first;
        first_free = cap = data.second;
    }
 
    template <typename InputIterator>
    Vec(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()) : alloc(alloc)
    {
        auto data = alloc_n_copy(first, last);
        elements = data.first;
        first_free = cap = data.second;
    }
 
    Vec(const Vec& other) : Vec(other.begin(), other.end()) {}
 
    Vec(const Vec& other, const Allocator& alloc) : Vec(other.begin(), other.end(), alloc) {}
 
    Vec(Vec&& other) noexcept(noexcept(Allocator()))
            : elements(other.elements), first_free(other.first_free), cap(other.cap)
    {
        other.elements = other.first_free = other.cap = nullptr;
    }
 
    // TODO: fix
//    Vec(Vec&& other, const Allocator& alloc) noexcept(noexcept(Allocator()))
//    : Vec(std::forward<Vec>(other)), alloc(alloc) {}
 
    Vec(std::initializer_list<T> init, const Allocator& alloc = Allocator())
            : Vec(init.begin(), init.end(), alloc) {}
 
    ~Vec() { free(); }
 
    Vec& operator=(const Vec& other)
    {
        auto data = alloc_n_copy(other.begin(), other.end());
        free();
        elements = data.first;
        first_free = cap = data.second;
        return *this;
    }
 
    Vec& operator=(Vec&& other)
            noexcept(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value
                     || std::allocator_traits<Allocator>::is_always_equal::value)
    {
        if (this != &other) {
            free();
            elements = other.elements;
            first_free = other.first_free;
            cap = other.cap;
            other.elements = other.first_free = other.cap = nullptr;
        }
        return *this;
    }
 
    Vec& operator=(std::initializer_list<T> init)
    {
        auto data = alloc_n_copy(init.begin(), init.end());
        free();
        elements = data.first;
        first_free = cap = data.second;
        return *this;
    }
 
    // iterators:
    iterator                begin() noexcept            { return elements; }
    const_iterator          begin() const noexcept      { return elements; }
    iterator                end() noexcept              { return first_free; }
    const_iterator          end() const noexcept        { return first_free; }
    
    reverse_iterator        rbegin() noexcept           { return std::make_reverse_iterator(first_free); }
    const_reverse_iterator  rbegin() const noexcept     { return std::make_reverse_iterator(first_free); }
    reverse_iterator        rend() noexcept             { return std::make_reverse_iterator(elements); }
    const_reverse_iterator  rend() const noexcept       { return std::make_reverse_iterator(elements); }
    
    const_iterator          cbegin() noexcept           { return elements; }
    const_iterator          cend() noexcept             { return first_free; }
    const_reverse_iterator  crbegin() const noexcept    { return std::make_reverse_iterator(first_free); }
    const_reverse_iterator  crend() const noexcept      { return std::make_reverse_iterator(elements); }
 
    template <typename InputIterator>
    void assign(InputIterator first, InputIterator last)
    {
        auto data = alloc_n_copy(first, last);
        free();
        elements = data.first;
        first_free = cap = data.second;
    }
 
    void assign(size_type count, const T& value)
    {
        auto data = alloc_n_fill(count, value);
        free();
        elements = data.first;
        first_free = cap = data.second;
    }
 
    void assign(std::initializer_list<T> init)
    {
        auto data = alloc_n_copy(init.begin(), init.end());
        free();
        elements = data.first;
        first_free = cap = data.second;
    }
 
    allocator_type get_allocator() const noexcept { return alloc; }
 
    // capacity:
    size_type size() const noexcept { return first_free - elements; }
    size_type capacity() const noexcept { return cap - elements; }
 
    void swap(Vec& other)
     noexcept(std::allocator_traits<Allocator>::propagate_on_container_swap::value ||
              std::allocator_traits<Allocator>::is_always_equal::value)
    {
 
    }
 
private:
    allocator_type alloc;
    pointer elements =    nullptr;
    pointer first_free =  nullptr;
    pointer cap =         nullptr;
 
    //allocate memory and copy
    std::pair<pointer, pointer> alloc_n_copy(const_pointer begin, const_pointer end) {
        auto data = alloc.allocate(end - begin);
        return {data, std::uninitialized_copy(begin, end, data)};
    }
 
    //allocate memory and fill
    std::pair<pointer, pointer> alloc_n_fill(size_type count, const_reference val = value_type())
    {
        auto data = alloc.allocate(count);
        return {data, std::uninitialized_fill_n(data, count, val)};
    }
 
    //free memory
    void free() {
        if (elements) {
            for (auto p = first_free; p != elements;)
                alloc.destroy(--p);
            alloc.deallocate(elements, cap - elements);
        }
    }
 
    void chk_n_alloc(size_type count) { if (count >= capacity()) reallocate(count); }
 
    void reallocate(size_type count)
    {
        auto new_capacity = size() ? 2 * count : 1;
        auto first = alloc.allocate(new_capacity);
        auto last = std::uninitialized_copy(std::make_move_iterator(begin()), // TODO overload to noexept
                                            std::make_move_iterator(end()),
                                            first);
        free();
        elements = first;
        first_free = last;
        cap = elements + new_capacity;
    }
};
 
int main()
{
    Vec<int> v(10);
}
Смотрел объявления метотов в стандарте и на cppreference.com, реализацию дописал сам. Как вот например мне правильно реализовать метод swap с учетом аллокатора?
0
Игогошка!
1801 / 708 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
27.06.2016, 01:14 4
notAll, что мешает в качестве примера открыть код стандартной либы и посмотреть, как там это реализовано в том же векторе?
0
495 / 209 / 70
Регистрация: 27.05.2016
Сообщений: 557
27.06.2016, 14:35  [ТС] 5
Уж слишком сложно там все написано Ладно, буду разбираться.

Добавлено через 13 часов 2 минуты
Ну, в общем, порылся в исходниках std::vector, - там примерно так пользуются свойствами аллокатора (на примере оператора перемещения):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
template <typename T, typename Allocator = std::allocator<T>>
class Vec
{
public:
    typedef typename std::allocator_traits<Allocator> Alloc_traits;
 
 
    Vec& operator =(Vec&& other)
    noexcept(Alloc_traits::propagate_on_container_move_assignment::value
             || std::is_empty<Allocator>::value)
    {
        constexpr bool move_storage = Alloc_traits::propagate_on_container_move_assignment::value
                                      || std::is_empty<Allocator>::value;
 
        _m_move_assign(std::move(other), std::integral_constant<bool, move_storage>());
        return *this;
    }
 
private:
 
    void _m_move_assign(Vec&& other, std::true_type) noexcept
    {
        // Constant-time move assignment when source object's memory can be
        // moved, either because the source's allocator will move too
        // or because the allocators are equal.
    }
 
    void _m_move_assign(Vec&& other, std::false_type)
    {
        // Do move assignment when it might not be possible to move source
        // object's memory, resulting in a linear-time operation.
    }
};
Не знаю почему я раньше туда не заглядывал

Добавлено через 7 минут
Кстати, нашел интересный документ по C++17: Deprecating Vestigial Library Parts in C++17 - там многое хотят запретить в std::allocator.
0
27.06.2016, 14:35
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
27.06.2016, 14:35
Помогаю со студенческими работами здесь

Что такое compile-time алгоритмы и для чего они нужны?
А есть от них хоть какая-то практическая польза? По-моему нет

Динамические структуры. Для чего они?
Прошли вчера в вузе динамические структуры, и пока только один вид - очередь. Но я так и не понял...

Try - catch - throw для чего они
Есть код программы принимающей температуру и заводит в вектор, она еще не доработана, Страуструп...

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
5
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru