为LValk和rValk实现包装的C++范围

为LValk和rValk实现包装的C++范围,c++,qt,for-loop,c++17,rvalue-reference,C++,Qt,For Loop,C++17,Rvalue Reference,我已经为循环的范围实现了一个小的helper包装器,它允许迭代关联Qt容器(如QMap和QHash)的键和值,将每一对提取为结构化绑定,例如: const QMap<int, QString> digitMap = { {1, "one"}, {2, "two"}, {3, "three"} }; for (auto [intKey, strVal] : make_keyval(digitMap)) { qDebug

我已经为循环的范围实现了一个小的helper包装器,它允许迭代关联Qt容器(如QMap和QHash)的键和值,将每一对提取为结构化绑定,例如:

const QMap<int, QString> digitMap = { {1, "one"}, {2, "two"}, {3, "three"} };
for (auto [intKey, strVal] : make_keyval(digitMap)) {
    qDebug() << intKey << "->" << strVal;
}
这似乎对正则变量和临时变量都适用。对于临时对象,m_rvalueContainer用于在迭代期间存储移动的临时对象,然后由m_containerRef引用。在正则变量的情况下,我们只将左值引用直接存储在m_containerRef中,而不将m_rvalueContainer设置为unset。我验证了在每种情况下都会调用正确的构造函数,并且只有在循环范围完成后才会销毁临时构造函数

所以我的问题很简单:这是我的包装器的正确实现,还是我遗漏了什么?或者也许有一件我没想到的案子

请注意,在我的初始版本中,我将m_-valuecontainer作为常规值,但我认为在左值的情况下,这将导致空容器的实例化,尽管这对于Qt容器来说是一个便宜的操作,所以我将其替换为unique_-ptr以确保在这种情况下没有开销。是的,它仍然会初始化为null ptr,但这是可以忽略的

任何其他注释或建议?

因为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_引用来成功地获得左值情况下的迭代器类型。

因为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_引用来成功地获得左值情况下的迭代器类型。

临时sometype.toMap不是一直保持到循环的范围结束吗?需要这些机器吗?@RobertAndrzejuk sometype.toMap返回的临时文件没有开始/结束函数,所以我们需要包装它。这意味着在这种情况下,包装器的生存期将延长,而不是容器。因此,如果容器是右值,包装器需要负责延长容器的生存期。临时sometype.toMap不是一直保持到for循环的范围结束吗?需要这些机器吗?@RobertAndrzejuk sometype.toMap返回的临时文件没有开始/结束函数,所以我们需要包装它。这意味着在这种情况下,包装器的生存期将延长,而不是容器。因此,如果容器是右值,包装器需要负责延长容器的生存期。@idclev463035818是,但OP不希望返回容器。他希望返回容器的包装,在这种情况下,我们需要在包装中存储对原始容器的引用。如果原始值是右值,我们需要确保引用没有悬空。临时值将传递给包装器的构造函数,并在包装器构造完成后销毁。事实上,我们在包装器中存储对临时文件的引用不会延长它的生命周期@切里奇似乎是这样。我自己从未使用过Qt,所以不熟悉它的功能。我只是回答这个问题。即使该类不能解决使用右值的问题。在循环之前,您需要使用init语句或将容器存储在变量中。@RomainPokrzywka如果希望在右值情况下移动容器,可以将构造函数更改为key_value_range_iteratorconst C&&container:m_containerRefstd::forwardcontainer{}。如果C是QMap&,它将是const QMap&&&引用折叠将使其成为QMap&,对于QMap,您将得到一个右值引用QMap&。std::forward将在右值情况下移动。@idclev463035818是,但OP不希望返回容器。他希望返回容器的包装,在这种情况下,我们需要在包装中存储对原始容器的引用。如果原始值是右值,我们需要确保引用没有悬空。临时值将传递给包装器的构造函数,并在包装器构造完成后销毁。事实上,我们在包装器中存储对临时文件的引用不会延长它的生命周期@切里奇似乎是这样。我自己从未使用过Qt,所以不熟悉它的功能。我只是回答这个问题。即使该类不能解决使用
右值。在循环之前,您需要使用init语句或将容器存储在变量中。@RomainPokrzywka如果希望在右值情况下移动容器,可以将构造函数更改为key_value_range_iteratorconst C&&container:m_containerRefstd::forwardcontainer{}。如果C是QMap&,它将是const QMap&&&引用折叠将使其成为QMap&,对于QMap,您将得到一个右值引用QMap&。std::forward将在右值情况下移动。