c++ - 为左值和右值实现 C++ range-for 包装器

标签 c++ qt for-loop c++17 rvalue-reference

我已经为 range-for 循环实现了一个小助手包装器,它允许迭代关联 Qt 容器的键和值,例如 QMapQHash ,将每一对提取为结构化绑定(bind),例如:

const QMap<int, QString> digitMap = { {1, "one"}, {2, "two"}, {3, "three"} };
for (auto [intKey, strVal] : make_keyval(digitMap)) {
    qDebug() << intKey << "->" << strVal;
}
Qt 容器不支持开箱即用,因为它们需要使用一对特定的 constKeyValueBegin()constKeyValueEnd()方法(让我们假设只有非变异迭代)。所以我的想法是编写一个简单的包装器类型,提供一对常规的 begin()end()简单地调用容器上的 keyValue 的方法。
这很容易,但作为一个延伸目标,我还想让包装器可用于临时对象,例如 make_keyval(sometype.toMap()) .那里的主要挑战是通过迭代结束来延长临时的生命周期,因为我正在通过一个代理对象。这是我想出的解决方案:
template<typename C>
struct key_value_range_iterator {
    key_value_range_iterator(const C& container) : m_rvalueContainer(nullptr), m_containerRef(container) {}
    key_value_range_iterator(const C&& container) : m_rvalueContainer(std::make_unique<C>(std::move(container))), m_containerRef(*m_rvalueContainer) {}

    typename C::const_key_value_iterator begin() const { return m_containerRef.constKeyValueBegin(); }
    typename C::const_key_value_iterator end() const { return m_containerRef.constKeyValueEnd(); }

private:
    const std::unique_ptr<C> m_rvalueContainer;
    const C& m_containerRef;
};

template<typename C>
auto make_keyval(C&& container) { return key_value_range_iterator(std::forward<C>(container)); }
这似乎对常规变量和临时变量都有效。对于临时工,m_rvalueContainer用于在迭代期间存储移动的临时值,然后由 m_containerRef 引用.在常规变量的情况下,我们只是将左值引用存储在 m_containerRef 中。直接离开m_rvalueContainer未设置。我验证了在每种情况下都调用了正确的构造函数,并且临时构造函数仅在 range-for 循环完成后被销毁。
所以我的问题很简单:这是我的包装器的正确实现,还是我错过了什么?或者也许有一个我没有想到的角落案例?
请注意,在我的初始版本中,我有 m_rvalueContainer作为一个常规值,但我认为这最终会在左值情况下实例化一个空容器(尽管这对于 Qt 容器来说是一个便宜的操作),所以我将它替换为 unique_ptr以确保在这种情况下没有开销。是的,它仍然最终将其初始化为 nullptr,但这可以忽略不计。
还有其他意见或建议吗?

最佳答案

由于make_keyval知道传入的对象是左值还是右值,您可以将该参数传递给您的包装器。

#include <type_traits>

template<typename C>
struct key_value_range_iterator {
    key_value_range_iterator(const C container) : m_containerRef(container) {}

    using iterator = typename std::remove_reference_t<C>::const_key_value_iterator;

    iterator begin() const { return m_containerRef.constKeyValueBegin(); }
    iterator end() const { return m_containerRef.constKeyValueEnd(); }

private:
    const C m_containerRef;
};

template<typename C>
auto make_keyval(C&& container) { return key_value_range_iterator<C>(std::forward<C>(container)); }
传递左值时 C推断为 QMap&使包装器保持引用。
传递右值时 C推断为 QMap使包装器将右值移动到它的成员中。
由于C可以是QMap&我们需要使用 std::remove_reference为左值情况成功获取迭代器类型。

关于c++ - 为左值和右值实现 C++ range-for 包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62842491/

相关文章:

c++ - 使用 SDL_CreateRGBSurface 创建新的 SDL_Surface 时遇到困难

c++ - QRegExp - 如何获取两个 HTML 标签之间的特定文本

for-loop - 在 for 循环中使用 AtoI 时出错

C++ 如何将十进制数转换为二进制数?

c++ - 如何在Qt代码中自定义一个QPushbutton

c - 在 C 中启动嵌套 for 循环以迭代参数的问题

c++ - Webkit GTK : Stop loading and reset web view

c++ - 将 std::error_code 与整数进行比较

c++ - 您如何使用非 OO 语言进行继承?

c++ - QMenuBar 和 QMenu 在 Mac OS X 中不显示