c++ - iterator::operator* 的标准接口(interface)返回的内容多于 T& 和 std::pair

标签 c++ iterator c++14

每个数据结构的迭代器都有不同类型的返回值 operator*()例如:-

  • 最像数组 DataStructure<T> :返回T&
  • std::unordered_map<K,T> :返回std::pair<const K, T>&
  • const std::unordered_map<K,T> :返回const std::pair<K, T>&

如果我的 iterator::operator*() 数据类型应该是什么想要返回更复杂的东西( std::pair 还不够)?

示例

我有一个自定义的 3D 数据结构 Map3D<T>就像一张 map (int,int,int)->T

为了实现上述要求,我创建了一个新类Bundle3D<T>

template<class T>class Bundle3D {
    //getInternalIndex1D()
    //getCoordinate()
    //getValue()
};

Map3D<T>iterator::operator*会有这样的签名:-

Bundle3D<T> iterator::operator*(){ ... }

它的用法如下:-

Map3D<float> map;
for(auto& ele:map){  //"ele" has some special type named "Bundle"
    int keyInternal1DIndex=ele.getInternalIndex1D();
    Vector3Int& key=ele.getCoordinate();
    float& value=ele.getValue();
}

效果很好,但我认为我的代码没有标准化
换句话说,Bundle3D<T> , getInternalIndex1D() , getCoordinate()getValue()是我瞎起的名字。

在实际情况中,我创建了很多自定义数据结构来生成如此奇怪的迭代器。

问题

有没有std::/iterator::operator*()的返回值的标准类型,当 T&std::pair是不足够的?

我已经怀疑这个问题好几个月了,但又很不愿意问。
如果这个问题需要改进,请评论。

编辑

(澄清这个词标准化 - 这部分是我的主观想法。)

我觉得大多数语言中的所有类型的标准集合,例如:-

  • java.util.Collection/ArrayList在Java中
  • std::unordered_map/vector在 C++ 中

它们的迭代器都具有函数 getValue() 的签名或operator*返回 TStandardPair<index,T> - 没有其他类型。

多年来,我坚信这是我真正应该遵守的规则或强烈的惯例/传统。

必有理由。
但是,我不确定它是什么,因为我可能对 C++ 很陌生。

如果我设计的软件返回一些奇怪的东西(例如上例中的 Bundle3D<T>),我想我会因为非常规设计而受到严厉的惩罚。 (例如没有 .first 字段)

最佳答案

你现在拥有的一切都还好。我只想指定一件我认为不太 C++ 风格的事情,并且实际上可能会损害您将来的标准化选项。

这是 setter/getter 。显然你对Java有很深的根基,并且公共(public)成员的概念在Java编程中是令人深恶痛绝的,以至于存在“bean”的概念。我不想在这里批评 Java,它是一种很好的语言,有自己很好的习惯用法。

但这是 C++,有它自己的编程习惯。您显然注意到了对 std::pair 内容的直接访问。之所以会这样,是有原因的。一对只是打包在一起的两个项目,这是其行为的总和。 setter/getter 只会妨碍。 :)
不需要它们,因为我们不是在处理捆绑在一起的两个项目的“抽象”,而是我们确实有一个具体的、诚实的 bundle 1

显然,我们可以捆绑两个以上的元素。这就是为什么我们有 std::tuple。现在,虽然对元组的所有访问确实都是通过名为 get 的非成员函数进行的,但这只是因为无法为任意大小的包的成员命名。 get 仍然返回对该元素的引用,因此您可以直接访问该包。

这个冗长的阐述很重要,因为即将推出的 C++1z 功能称为 "Structured Bindings" ,依赖于元组和对的标准行为,以及 C++ 程序员如何看待聚合。在此功能下, map 上的迭代将如下所示:

#include <map>
#include <iostream>

std::map<char const*, int> foo()
{
    return {
        { "foo", 3 },
        { "bar", 7 },
        { "baz", 1 },
    };
}

int main() {

    for (auto [key, val] : foo()) {
        std:: cout << "( " << key << ", " << val << " )\n";
    }

   return 0;
}

Live example

这同样适用于任何用户定义的数据包。如果成员是公共(public)的,则可以类似地绑定(bind)帖子中您自己的返回值类型:

struct Vector3Int {};

template<class T>
struct Bundle3D {
    int               internal_index_id;
    Vector3Int const &coord;
    T                &value;
};

int main() {
  Vector3Int vec;
  float val;
  Bundle3D<float> bundle{ 1, vec, val };

  auto[ idx_id, coord, value] = bundle;

  // coord = {}; // compile error since coord gets the cv qualifer

}

Live example

所以我的建议是,将 getter 留给 Java 代码,并使用聚合作为 C++ 的数据束。


1 更正式地说,是一个聚合。

关于c++ - iterator::operator* 的标准接口(interface)返回的内容多于 T& 和 std::pair,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43888016/

相关文章:

c++ - C++ 指针是否跨 EXE 和 DLL 工作

c++ - C++ 中的这种隐式转换是如何发生的?

c++ - 列出超出范围的删除迭代器

c++ - 迭代器类别

c++ - 为静态成员函数使用别名?

c++ - 为什么不能形成对 'decltype(auto)' 的引用

c++ - 为函数提供字符串或整数

c++ - 这是制作迭代器的可接受方式吗?

c++ - 是否可以将 constexpr std::array 转换为 std::integer_sequence?

C++ PlaySound() 错误