公开自定义stl风格迭代的首选方式是什么?
what is the preferred way to expose custom STL-style iteration?
(另请参阅是否有一种好方法可以避免在c++中为自定义类型手写所有十二个必需的容器函数?)
对于像
这样的类namespace JDanielSmith {
class C
{
const size_t _size;
const std::unique_ptr<int[]> _data;
public:
C(size_t size) : _size(size), _data(new int[size]) {}
inline const int* get() const noexcept { return _data.get(); }
inline int* get() noexcept { return _data.get(); }
size_t size() const noexcept { return _size; }
};
}
公开迭代的首选方式是什么?我应该写begin()
/end()
(和cbegin()
/cend()
)成员函数吗?
const int* cbegin() const {
return get();
}
const int* cend() const {
return cbegin() + size();
}
还是应该是非成员函数?
const int* cbegin(const C& c) {
return c.get();
}
const int* cend(const C& c) {
return cbegin(c) + c.size();
}
begin()
/end()
应该同时具有const
和非const
过载吗?
const int* begin() const {
return get();
}
int* begin() {
return get();
}
还有什么要考虑的吗?是否有工具/技术使这"容易得到正确"并减少样板代码的数量?
一些相关的问题/讨论包括:
- 自定义容器应该有自由的开始/结束功能吗?为什么在c++ 11中使用非成员的开始和结束函数?
- 何时使用
std::begin
和std::end
而不是容器特定版本
如果你想让你的类接口与STL一致,有一个标准描述了它们应该是什么样子。c++有"概念"的概念,它确定了给定类要充分实现一个概念的要求。这几乎成为c++11的一个语言特性。
您可能感兴趣的一个概念是容器概念。如您所见,为了满足容器概念的需求,您需要begin
、cbegin
、end
和cend
作为成员函数(以及其他功能)。
因为它看起来像是在数组中存储数据,所以您可能还对SequenceContainer感兴趣。
我选c。
这里的主要问题是std::begin()
实际上不能用ADL找到非成员begin()
。因此,真正的解决方案是编写自己的
namespace details {
using std::begin;
template <class C>
constexpr auto adl_begin(C& c) noexcept(noexcept(begin(c)))
-> decltype(begin(c))
{
return begin(c);
}
}
using details::adl_begin;
现在,不管你是把begin()
写为成员函数还是非成员函数,只要在任何地方使用adl_begin(x)
,它就会工作。对于标准容器和原始数组也是如此。这很方便地回避了成员与非成员的讨论。
是的,如果你想公开const
和非const
访问,你应该有const
和非const
的begin()
和朋友重载。
我建议创建两组函数——成员函数和非成员函数——以获得最大的灵活性。
namespace JDanielSmith {
class C
{
const size_t _size;
const std::unique_ptr<int[]> _data;
public:
C(size_t size) : _size(size), _data(new int[size]) {}
inline const int* get() const { return _data.get(); }
inline int* get() { return _data.get(); }
size_t size() const { return _size; }
int* begin() { return get(); }
int* end() { return get() + _size; }
const int* begin() const { return get(); }
const int* end() const { return get() + _size; }
const int* cbegin() const { return get(); }
const int* cend() const { return get() + _size; }
};
int* begin(C& c) { return c.begin(); }
int* end(C& c) { return c.end(); }
const int* begin(C const& c) { return c.begin(); }
const int* end(C const& c) { return c.end(); }
const int* cbegin(C const& c) { return c.begin(); }
const int* cend(C const& c) { return c.end(); }
}
如果您希望能够使用C
类型的对象作为std::begin
、std::end
、std::cbegin
和std::cend
的参数,则成员函数是必需的。
如果您希望能够使用C
类型的对象作为begin
, end
, cbegin
和cend
的参数,则非成员函数是必要的。ADL将确保在这些用法中找到非成员函数。
int main()
{
JDanielSmith::C c1(10);
{
// Non-const member functions are found
auto b = std::begin(c1);
auto e = std::end(c1);
for (int i = 0; b != e; ++b, ++i )
{
*b = i*10;
}
}
JDanielSmith::C const& c2 = c1;
{
// Const member functions are found
auto b = std::begin(c2);
auto e = std::end(c2);
for ( ; b != e; ++b )
{
std::cout << *b << std::endl;
}
}
{
// Non-member functions with const-objects as argument are found
auto b = begin(c2);
auto e = end(c2);
for ( ; b != e; ++b )
{
std::cout << *b << std::endl;
}
}
}
为了创建一个有效的迭代器,必须确保std::iterator_traits是有效的。这意味着你必须在其他事物中设置迭代器类别。
迭代器应该实现iterator()、iterator(iterator&&)、iterator(iterator constator &)、operator==、operator !=、operator++、operator++(int)、operator*、operator=和operator->。加上operator<如果可以,使用operator+(不能总是这样,例如链表)>
template <typename T>
class foo
{
public:
using value_type = T;
class iterator
{
public:
using value_type = foo::value_type;
using iterator_category = std::random_access_iterator_tag;
// or whatever type of iterator you have...
using pointer = value_type*;
using reference = value_type&;
using difference_type = std::ptrdiff_t;
// ...
};
class const_iterator
{
// ...
};
iterator begin() { /*...*/ }
iterator end() { /*...*/ }
const_iterator cbegin() const { /*...*/ }
const_iterator cend() const { /*...*/ }
/* ... */
};
参见:http://en.cppreference.com/w/cpp/iterator/iterator_traits了解创建有效迭代器所需的更多信息。(注意:您还需要某些属性成为有效的"容器",如.size())
理想情况下,你应该使用成员函数开始和结束,但这不是必需的…还可以重载std::begin和std::end。如果你不知道怎么做,我建议你使用成员函数。
你应该创建begin() const和end() const,但它应该是cbegin()的别名,绝不能和begin()一样!
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 在reactor中存储eventHandlers的最佳方式是什么
- 引用 std::any 或 not_yet_in_std::whatever 的惯用方式是什么?
- 在C++中,建议通过数组循环的方式是什么?
- DLL共享数据的推荐方式是什么
- 等待线程的最佳方式是什么
- 将uint8_t*buffer和size_tbufferlen从C++传递到C中的API函数的最佳方式是什么
- 只显示片段着色器的最佳方式是什么
- 复制文件的最佳方式是什么,以便我可以在复制过程中轻松取消复制?
- 在 c++ 中打印到控制台的最佳方式是什么?
- 在Qt Creator中应用代码更改的快捷方式是什么?
- 在C 中超负荷构造函数的合适方式是什么
- 执行随机开关函数的QT方式是什么连续两次使用相同情况的方法
- 在某些代码中覆盖方法的方式是什么?
- 为单个函数同时声明多个变量的最佳方式是什么
- Qt中数据类(模型)和视图/控制器类之间的数据通信的正确方式是什么
- 在c++中存储一个对象或不存储对象的首选方式是什么
- 在C 项目中包含库的不同方式是什么?
- 解释"Bit String"的最佳方式是什么
- 计算差异数据并通过网络发送的最佳方式是什么