Инициализируйте статический std :: map с не копируемым значением при встроенной инициализации в форме - PullRequest
0 голосов
/ 17 декабря 2018

Я хотел бы инициализировать статический std::map, где значение не может быть скопировано.Я позвоню своему классу ValueClass . ValueClass имеет std::unique_ptr в качестве частного члена, и я даже гарантирую, что ValueClass не будет копируемым, расширив non_copyable, который выглядит следующим образом:

class non_copyable {
public:
    non_copyable() = default;
protected:
    virtual ~non_copyable() = default;
private:
    non_copyable(const non_copyable&) = delete;
    non_copyable& operator=(const non_copyable&) = delete;
};

Теперь я пытаюсьопределить std :: map, используя мой класс в качестве значения:

static std::map<int, ValueClass> value_classes = {
    {0, ValueClass()},
    {1, ValueClass() }
};

Я получаю ошибку компиляции, когда initializer_list пытается скопировать этот класс.

Я пытался написать свой собственный make_map работает в течение этих выходных в течение многих часов, чтобы включить инициализацию без копирования, но мне не удалось.Я пытался это , , что и другие , но ни один из них не компилируется с Visual Studio 15.9.4.

Как я могу инициализировать статическийstd :: map, где копирование не принудительное, а инициализация выполняется в одной функции с использованием компилятора Visual Studio?

РЕДАКТИРОВАТЬ: Вот упрощенная версия сценария реальной жизни, где яя пытаюсь заставить это работать (простите за отсутствие соглашения об именах и несогласованность для случаев):

#include <iostream>
#include <map>

class non_copyable {
public:
    non_copyable() = default;
protected:
    virtual ~non_copyable() = default;
private:
    non_copyable(const non_copyable&) = delete;
    non_copyable& operator=(const non_copyable&) = delete;
};

class InnerValueClass : public non_copyable
{
public:
    InnerValueClass(const int inner_number) : inner_number_(inner_number) {  }
private:
    int inner_number_;
};

class ValueClass : public non_copyable
{
public:
    ValueClass(const int number1) : number1_(number1) {  }
    ValueClass(const bool condition) : condition_(condition), inner_value_(
        std::make_unique<InnerValueClass>(5)) {  }
private:
    int number1_{};
    bool condition_{};
    std::unique_ptr<InnerValueClass> inner_value_{};
};

/* Inline initialization of std::map copies, this is for initialization of non-copy types*/
template <typename TKey, typename TNonCopyableValue>
class make_map_by_moving
{
    typedef std::map<TKey, TNonCopyableValue> map_type;
    map_type map_;
public:
    make_map_by_moving(const TKey& key, TNonCopyableValue&& val)
    {
        map_.emplace(key, std::move(val));
    }
    make_map_by_moving<TKey, TNonCopyableValue>& operator()(const TKey& key, TNonCopyableValue&& val)
    {
        map_.emplace(key, std::move(val));
        return *this;
    }
    operator const map_type&()
    {
        return map_;
    }
};

static std::map<int, ValueClass> map =
        make_map_by_moving<int, ValueClass>
                (1, ValueClass(5))
                (2, ValueClass(true));
/* It goes on like this for hundreds of lines, so I really appreciate any
solution that leave me with a clean initialization rather than calling
functions on std::map */

int main() { }

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

Ответы [ 3 ]

0 голосов
/ 17 декабря 2018

Вы не можете сделать это напрямую, потому что initializer_list имеет const поддержку для всех своих элементов - и они должны быть скопированы из списка инициализатора в контейнер.Это, очевидно, требует копирования.К сожалению, невозможно исключить из списка инициализатора.

В C ++ 17, благодаря гарантированному разрешению копирования, вы можете сделать это:

std::map<int, non_copyable> get() {
    std::map<int, non_copyable> m;
    m.emplace(std::piecewise_construct, std::tuple(0), std::tuple());
    m.emplace(std::piecewise_construct, std::tuple(1), std::tuple());
    return m;
}

std::map<int, non_copyable> value_classes = get();

Этот код не выполняет никаких копий на non_copyable.Мы помещаем конструкцию внутрь map, а затем, потому что get() является prvalue, нет копирования / перемещения из get() в value_classes.m в get() - это объект value_classes.

Слегка подлый подход заключается в злоупотреблении try_emplace() для этого:

std::map<int, non_copyable> get() {
    std::map<int, non_copyable> m;
    m.try_emplace(0);
    m.try_emplace(1);
    return m;
}

try_emplace() принимает тип ключа сам по себе (так что вы можете просто передать int), а затем аргументы для значения для применения по отдельности, что обеспечивает гораздо менее подробный способ выполнения этого.

0 голосов
/ 17 декабря 2018

Вы просто не можете использовать initializer_list - move объект из non-copyable объекта.

Ваш класс удаляет copy constructor & assignment operator.Когда вы пытаетесь инициализировать map или любую другую container с помощью initializer_list, initializer_list строго заставляет вас ссылаться на LValue и запрещает RValue семантику перемещения или продвижения вперед.

Вот очень хорошая статья в блоге, которая объясняет все детали: knatten.org , а также похожий Q / A, найденный здесь .

0 голосов
/ 17 декабря 2018

Я думаю, вам нужно создать объект с insert_or_assign в функции, а затем вернуть его:

std::map<int, ValueClass> populate()
{
    std::map<int, ValueClass> value_classes;
    value_classes.insert_or_assign(std::make_pair(0, ValueClass());
    return value_classes;
}

И ваша инициализация станет:

std::map<int, ValueClass> value_classes = populate();

Но тогда этокласс имеет виртуальный деструктор, что означает, что вы на самом деле можете быть std::map<int, std::unique_ptr<ValueClass>>, а не картой реальных объектов (не знаете, для чего эти объекты будут использоваться?).

Редактировать послередактирование вопроса:

В этом случае Барри s suggestion is the one to follow, using emplace`:

std::map<int, ValueClass> populate()
{
    std::map<int, ValueClass> value_classes;
    value_classes.emplace(1, 5);
    return value_classes;
}

Также включает functional.

...