为左值和右值实现 C++ range-for 包装器
Implementing a C++ range-for wrapper for lvalues and rvalues
我已经为 range-for 循环实现了一个小助手包装器,它允许迭代关联 Qt 容器的键和值,如 QMap
和 QHash
,将每一对提取为结构化绑定,例如:
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
来成功获取左值情况下的迭代器类型。
我已经为 range-for 循环实现了一个小助手包装器,它允许迭代关联 Qt 容器的键和值,如 QMap
和 QHash
,将每一对提取为结构化绑定,例如:
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
来成功获取左值情况下的迭代器类型。