Можно ли избежать копирования во время инициализации std :: initializer_list без использования необработанных указателей? - PullRequest
0 голосов
/ 25 февраля 2019

Допустим, у меня есть несколько объявленных локально объектов, которые я хочу перебрать, используя синтаксис на основе диапазона.Это, кажется, работает хорошо, однако, кажется, что для помещения локальных объектов в initializer_list, выполняется копия.Это плохие новости для таких объектов, как std::shared_ptr, для которых (как я понимаю) увеличение счетчика ссылок является атомарной операцией.Единственный способ, как я думаю, этого можно избежать, используя необработанные указатели.

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptrInt1 = std::make_shared<int>(1);
    std::shared_ptr<int> ptrInt2 = std::make_shared<int>(2);
    /* in this loop, ptrInt1 and ptrInt2 are copied before they are binded
       to ptrInt, this is ugly since the reference counter needs to temporarily
       increased */
    for(const std::shared_ptr<int>& ptrInt : {ptrInt1, ptrInt2}) {
        std::cerr << *ptrInt << std::endl;
    }
    /* this solution works, but it feels somewhat ugly having to convert my smart
       pointers to raw pointers to avoid the copying, perhaps there is a better
       solution ?? */
    for(const int* rawPtrInt : {ptrInt1.get(), ptrInt2.get()}) {
        std::cerr << *rawPtrInt << std::endl;
    }
    return 0;
}

Есть ли способ перебора группы локально объявленных объектов без их копирования или использования необработанных указателей?

Ответы [ 3 ]

0 голосов
/ 25 февраля 2019

Вы можете использовать std::ref для построения списка std::reference_wrapper с.Это скрывает указатель и позволяет вам написать список как

for(const std::shared_ptr<int>& ptrInt : {std::ref(ptrInt1), std::ref(ptrInt2)}) {
    std::cerr << *ptrInt << std::endl;
}
0 голосов
/ 25 февраля 2019

Вот небольшой шаблон функции, который расширяет @ ответ NathanOliver и сокращает часть набора текста.

#include <array>
#include <functional>

// No rvalues, thanks to @NathanOliver for pointing that out:
template <class ...T>
auto crefRange(const T&&...) = delete;

template <class ...T>
auto crefRange(const T&... args)
{
   using First = std::tuple_element_t<0, std::tuple<T...>>;

   return std::array<First, sizeof...(T)>{{std::cref(args)...}};
}

Вы можете создать его экземпляр и вызвать его через

for(const std::shared_ptr<int>& ptrInt : crefRange(ptrInt1, ptrInt2))
    std::cerr << *ptrInt << std::endl;

Это не удастся, если вы создадите его с различными типами, но это ограничение идентично подходу std::initializer_list.

0 голосов
/ 25 февраля 2019

К сожалению, std::initializer_list плохо подходит для этой задачи.Поскольку

Базовый массив является временным массивом типа const T [N], в котором каждый элемент инициализируется копией ...

(https://en.cppreference.com/w/cpp/utility/initializer_list), он будет выполнять копии, а компилятор не будет их исключать.

Для этой задачи я бы сделал что-то еще. Вероятно, создайте класс шаблона переменной времени компиляции, основанный на указателях на базовые объекты.Я знаю, если вы хотите какой-то код.

...