除非存在类对象的“空”(无参数)构造函数,否则将对象添加到 std::map 不起作用

use*_*490 0 c++ stdmap stdmove

我正在研究一个开源应用程序的代码。我制作了这段代码的一个更简单的版本,以隔离困扰我的问题(尽管我对这段代码有几个问题,我希望 C++ 专家能够帮助我,我将从主要问题开始)。

主要问题:为什么我需要一个“空”构造函数(无参数)来分配给 std::map 的对象类?

主要思想(参见下面的代码)是将 Variant 类的实例分配给 a std::map(其键是 a std::string)。这是代码:

#include <iostream>
#include <map>
#include <string>
#include <memory>

struct Data
{
public:
    Data(const void* data, size_t bytes) : bytes(bytes)
    {
        ptr = malloc(bytes);
        memcpy(ptr, data, bytes);
        std::cout << "in ctor of Data" << std::endl;
    }
    ~Data() { free(ptr); std::cout << "in dtor of Data" << std::endl; }
    void* ptr{ nullptr };
    size_t bytes;
};

struct DataStream 
{
public:
    DataStream(const std::shared_ptr<Data>& ptr, size_t size) : ptr(ptr), size(size)
    { std::cout << "in ctor of DataStream" << std::endl; }
    std::shared_ptr<Data> ptr;
    size_t size;
};

struct Variant
{
public:
    enum Type
    {
        EMPTY,
        TYPE1 = 5,
        TYPE2 = 10
    };
    ~Variant() { std::cout << "in dtor of Variant" << std::endl; }
    
    // XXX If this ctor does NOT exist, the code doesn't compile XXX
    Variant() : type(EMPTY) { std::cout << "in ctor of Variant" << std::endl; }
    
    Variant(const int& n, Type type) : n(n), type(type) {}
    Variant(const std::shared_ptr<Data>& data, Type type, size_t size) : type(type), data(std::make_shared<DataStream>(data, size))
    { std::cout << "in ctor of Variant (ptr to typed array)" << std::endl; }
    Type type;
    int n;
    std::shared_ptr<DataStream> data;
};

struct Params
{
public:
    void add(const std::string& name, const Variant& data) { params[name] = data; }
    const Variant& operator[] (const std::string& name) { return params[name]; }
    std::map<std::string, Variant> params;
};

struct Handle
{
public:
    Params params;
    void set(const std::string& name, const Variant& data) { params.add(name, data); }
};

int main()
{
    Handle* handle = new Handle();

    char data_i[3] = { 'a', 'b', 'c' };
    std::shared_ptr<Data> data = std::make_shared<Data>(data_i, 3);
    handle->set("testC", Variant(data, Variant::TYPE1, 3));
    std::cout << "use_count data " << handle->params["testC"].data->ptr.use_count() << std::endl;
    std::cout << "Variant type " << handle->params["testC"].type << std::endl;

    delete handle;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我不向类添加一个不带任何参数的构造函数(我称之为空构造函数),则代码将无法编译。我收到以下错误消息:

test3.cpp:52:68: note: in instantiation of member function 'std::map<std::basic_string<char>, Variant>::operator[]' requested here
    void add(const std::string& name, const Variant& data) { params[name] = data; }
                                                                   ^
test3.cpp:29:8: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided
struct Variant
       ^
test3.cpp:40:5: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
    Variant(const int& n, Type type) : n(n), type(type) {}
    ^
test3.cpp:41:5: note: candidate constructor not viable: requires 3 arguments, but 0 were provided
    Variant(const std::shared_ptr<Data>& data, Type type, size_t size) : type(type), data(std::make_shared<DataStream>(data, size))
    ^
Run Code Online (Sandbox Code Playgroud)

我对这里发生的事情的理解是有限的。我确实知道我正在将 Variant 类的实例分配给映射,并且必须在代码的此时创建一​​个实例。因此,将调用对象类的构造函数。说得通。由于它没有参数,因此需要使用不带任何参数的构造函数。很公平。因此,我添加了不带参数的构造函数行。代码编译。然后,我输出存储在映射中的对象的类型,我可以看到,即使调用不带参数的构造函数来创建该对象,它仍然具有原始对象的类型(5 而不是 0)。

这就是我想要解释的一点。main()即使在分配该对象的副本时使用了不带参数的构造函数,它如何实际复制原始对象(在 中创建的对象)的内容std::map

附属问题#1):

如果我查看该 Variant 对象的构造函数/析构函数的顺序,我会得到以下结果:

in ctor of Data
in ctor of DataStream
in ctor of Variant (ptr to typed array)
in ctor of Variant
in dtor of Variant
use_count data 2
Variant type 5
in dtor of Variant
in dtor of Data
Run Code Online (Sandbox Code Playgroud)

原来的代码中,add方法是:

void add(const std::string& name, Variant data) { params[name] = data; }
Run Code Online (Sandbox Code Playgroud)

const ref 甚至不存在。顺序是:

in ctor of Data
in ctor of DataStream
in ctor of Variant (ptr to typed array)
in ctor of Variant
in dtor of Variant
in dtor of Variant
use_count data 2
Variant type 5
in dtor of Variant
in dtor of Data
Run Code Online (Sandbox Code Playgroud)

Variant 的析构函数被调用了 3 次,但构造函数只被调用了两次!不确定在这种情况下我缺少哪个构造函数。

但无论如何,我的问题是:从我在这里创建临时变量的那一刻起:

handle->set("testC", Variant(data, Variant::TYPE1, 3));
Run Code Online (Sandbox Code Playgroud)

我能否以某种方式确保在将该对象分配给映射之前不会创建该对象的副本?我尝试过添加一堆“std::move那里”和“那里”,但似乎没有什么区别。我只是认为这些副本不一定是强制性的,并且一定有办法避免它们。对于这两个问题,您的导师的意见将不胜感激。

vll*_*vll 6

operator[]DefaultConstructible如果键不存在,则要求类型为。

在线的params[name] = data;

  1. operator[]使用默认构造函数创建一个元素
  2. operator[]返回对该元素的引用
  3. data使用复制构造函数分配给引用

在您的情况下,步骤 1 失败,因为没有默认构造函数。

C++17 添加了insert_or_assign(),不要求类型为DefaultConstructible