10

我想初始化一个std::map值不可复制的静态变量。我会打电话给我的班级ValueClassValueClass有一个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 编译。

如何使用 Visual Studio 编译器在不强制复制的情况下初始化静态 std::map 并在一个函数中统一初始化?

编辑: 这是我试图让它工作的现实生活场景的简化版本(请原谅我缺乏命名约定和案例不一致):

#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函数的解决方案,换句话说就是内联初始化,提供的答案是函数调用的命令式解决方案。

4

3 回答 3

7

您不能直接执行此操作,因为它的所有元素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()是纯右值,没有从get()into复制/移动value_classesmget() 对象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),然后分别获取值的参数,这使得实现这一点的方式更加简洁。

于 2018-12-16T21:44:39.743 回答
4

我认为您需要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>>而不是实际对象的映射(不确定这些对象将用于什么?)。

问题编辑后编辑:

在这种情况下,Barry s suggestion is the one to follow, usingemplace`:

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

还包括functional.

于 2018-12-16T21:42:39.667 回答
3

你根本无法使用initializer_listmove一个对象到一个non-copyable对象。

您的班级删除了copy constructor& assignment operator。当您尝试使用 an初始化您的map或任何其他人时,严格强制您引用 an并禁止移动或转发语义。containerinitializer_listinitializer_listLValueRValue

这是一篇非常好的博客文章,解释了所有细节:knatten.org以及在此处找到的类似 Q/A 。

于 2018-12-16T23:21:42.180 回答