繁体   English   中英

这个结构浅拷贝的奇怪行为?

[英]Strange behaviour with this struct shallow copy?

我已经定义了以下数据结构,其目的是提供一个 get_next() 方法,该方法返回指向下一个 required_configuration 的迭代器。 如果required_explorations为空或已到达末尾,则返回它指向开头。

#include <iostream>
#include <map>
#include <string>
#include <unordered_map>

using configuration_model = std::unordered_map<std::string, std::string>;

struct doe_model {

    inline bool add_config(const std::string& config_id,
        const configuration_model& config,
        const int required_number_of_observations)
    {
        bool assignment_took_place = !required_explorations.insert_or_assign(config_id, config).second || !number_of_explorations.insert_or_assign(config_id, required_number_of_observations).second;
        next = required_explorations.end();
        return assignment_took_place;
    }

    inline void update_config(const std::string& config_id)
    {
        number_of_explorations.at(config_id)--;

        // remove the configuration in case we exausted all the explorations
        if (number_of_explorations.at(config_id) <= 0)
            remove_config(config_id);
    }

    inline void remove_config(const std::string& config_id)
    {
        required_explorations.erase(config_id);
        number_of_explorations.erase(config_id);
    }

    // this method returns the next configuration to explore
    // NOTE: the caller MUST check the pointer first.
    inline std::map<std::string, configuration_model>::iterator get_next()
    {
        // we may have an empty map or one with only a single configuration left
        if (required_explorations.empty() || next == required_explorations.end()) {
            std::cout << "Required explorations is empty or next at the end(), "
                         "returning the first pointer."
                      << std::endl;
            next = required_explorations.begin();
            return next;
        }

        next++;

        std::cout << "Returning a next pointer normally." << std::endl;
        return next;
    }

    // key is the configuration_id
    std::map<std::string, configuration_model> required_explorations;
    std::map<std::string, configuration_model>::iterator next;
    std::map<std::string, int> number_of_explorations;
};

我不知道它是为具有这种结构的赋值运算符生成的默认浅拷贝,但测试它我认为问题取决于next迭代器拷贝。

运行它会按预期返回0 (这是第一个元素)。

int main()
{
    doe_model doe;
    doe.add_config("0", { { "threads", "29" } }, 4);
    doe.add_config("1", { { "threads", "23" } }, 4);
    doe.add_config("2", { { "threads", "20" } }, 4);
    doe.add_config("3", { { "threads", "21" } }, 4);
    doe.add_config("4", { { "threads", "22" } }, 4);

    auto it = doe.get_next();
    std::cout << it->first << "\t" << std::endl;
}

但运行以下返回4即结束。

int main()
{
    doe_model doe;
    doe.add_config("0", { { "threads", "29" } }, 4);
    doe.add_config("1", { { "threads", "23" } }, 4);
    doe.add_config("2", { { "threads", "20" } }, 4);
    doe.add_config("3", { { "threads", "21" } }, 4);
    doe.add_config("4", { { "threads", "22" } }, 4);

    doe_model new_doe = doe;

    auto it = new_doe.get_next();
    std::cout << it->first << "\t" << std::endl;
}

有人可以让我了解幕后发生的事情吗?

更新这里是doe_model使用的真实示例。 有一些数据成员和其他 function 调用我不打算讨论,因为它们并不是真正需要的,以便了解如何使用doe_model send_configuration被调用时,它需要从 doe 结构中提取一个新的配置,因此需要get_next方法来实现该任务返回一个新的配置(与以前的不同,并且仍然具有number_of_explorations > 0 )。

  void send_configuration(const client_id_t &name)
  {
    if (num_configurations_sent_per_iteration <= num_configurations_per_iteration)
    {
      auto configuration = doe.get_next();
      if (configuration != doe.required_explorations.end())
      {
        remote->send_message(
            {MESSAGE_HEADER + "/" + app_id.app_name + "^" + app_id.version + "^" + app_id.block_name + "/" + name + "/explore",
             configuration_to_json(configuration->second)});


        doe.update_config(configuration->first);

        num_configurations_sent_per_iteration++;
      }
    }
  }

next; 被复制,仍然引用另一个实例的required_explorations

当您调用get_next()时,比较next == required_explorations.end()具有未定义的行为,它仅针对同一容器1的迭代器定义。

另外:在required_explorations为空的情况下,返回required_explorations.begin()并取消引用它也有未定义的行为。

  1. 如果您使用的是 C++14 或更高版本,您可以将您的迭代器与值初始化的迭代器进行比较,它的行为类似于所有容器的end 在 C++11 中,您需要容器中的第二个迭代器以确保安全。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM