【问题标题】:Keep Class Type after calling `operator[]` in nested Class在嵌套类中调用 `operator[]` 后保留类类型
【发布时间】:2019-12-14 00:20:39
【问题描述】:

下面的类是一个顶层类,它带来了nlohman::json的所有好处,但提供了额外的功能。

#include <nlohmann/json.hpp>

class Other { /* ... */ };

class AbstractData : public nlohmann::json
{
public:
    AbstractData (const nlohmann::json& json) : nlohmann::json(json) { }

    Other createOther(const char* key) { /* create Other class using key */ }
    std::string toString() { /* convert to string */ }
    /* etc. */
};


但是我在使用operator[] 时遇到了问题。 默认我们有

AbstractData a;
auto& val = a["some_key"];  // val is nlohman::json::value_type&

因此val 失去了所有额外的功能。

当我们提供类函数operator[]

const AbstractData& AbstractData::operator[](const char* key) const
{
    return nlohmann::json::operator[](key);
}

然后

AbstractData a;
auto& val = a["some_key"];  // val is AbstractData&

按预期工作。但是为了实现这一点,调用了复制构造函数AbstractData (const nlohmann::json&amp; json)(这对于大对象来说效率很低)。这首先违背了返回引用的目的。

我看到过类似Add a method to existing C++ class in other file 的问题,但他们没有为我的具体问题提供帮助。

有什么建议吗?

【问题讨论】:

  • https://github.com/nlohmann/json#design-goals 不包括可扩展性。不要将继承用于扩展!

标签: c++ function class inheritance c++17


【解决方案1】:

我会放弃继承并完全包装数据。

为什么?因为当您需要第二个AbstractData 时,您将不得不持有并可能复制 json 值。如果您包装 json 数据而不是使用继承,那么您可以充当 json 数据的视图。

class AbstractData {
    // view over json
    nlohmann::json const* _json_ptr;

    auto json() -> nlohmann::json const& {
        return *_json_ptr;
    }

public:
    AbstractData(nlohmann::json const& json) : nlohmann::json(&json) {}

    Other createOther(const char* key) {
        /* create Other class using key */
    }

    std::string toString() {}

    auto operator[](const char* key) const -> AbstractData {
        return AbstractData{&(json()[key])};
    }
};

如您所见,您可以安全地按值返回,因为您的类只保存指向您的值的指针并且复制起来很便宜。


如果您还希望您的类成为所有者,您可以将 json 存储为 const 共享指针:

class AbstractData {
    using root_t = std::shared_ptr<nlohmann::json const>;
    // owner of the json root.
    root_t _root;

    // view over json
    nlohmann::json const* _json_ptr;

    auto json() -> nlohmann::json const& {
        return *_json_ptr;
    }

    AbstractData(nlohmann::json const& json, root_t root) :
        _root(root), _json_ptr(&json) {}

public:
    struct new_root_t {} static constexpr new_root{};

    AbstractData(new_root_t, nlohmann::json json) :
        _root{std::make_shared<nlohmann::json const>(std::move(json))}, _json_ptr{_root.get()} {}

    auto operator[](const char* key) const -> AbstractData {
        // always pass down the root, so someone will own it
        return AbstractData{json()[key], _root};
    }
};

Live example


附带说明,您的行为未定义:

// return by reference?
const AbstractData& AbstractData::operator[](const char* key) const {
    // construct a new, local value
    // the local value is destroyed then returned
    return nlohmann::json::operator[](key);
}

我强烈建议在这里按值返回。

【讨论】:

  • 我喜欢这个主意,但nlohmann::json const* _json_ptr 实际住在哪里?谁拥有它?如果我调用AbstractData(nlohmann::json{}),它将存储地址,然后销毁对象(类似于您在我的 UB 案例中指出的内容)。至于operator[],理想情况下这应该总是返回一个引用,因为这是有效的方式(不构造副本等)
  • @Phil-ZXX 哦,事实上这真的很糟糕,你有双重免费和东西,这不是你使用共享指针的方式。
  • @Phil-ZXX 我添加了实时示例。创建新树时,它现在是显式的,我使用共享指针修复了双重释放。
  • 啊,我明白你做了什么。将 ptr 构造函数设为私有是个好主意。
猜你喜欢
  • 2021-03-11
  • 2020-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 2019-03-23
相关资源
最近更新 更多