公开自定义 STL 样式迭代的首选方法是什么?
what is the preferred way to expose custom STL-style iteration?
对于class如
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();
}
还有其他需要考虑的事情吗?是否有 tools/techniques 来实现 "easy to get right" 并减少样板代码的数量?
一些相关的questions/discussion包括:
- Should custom containers have free begin/end functions?
- Why use non-member begin and end functions in C++11?
- When to use
std::begin
and std::end
instead of container specific versions
我选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
和非 [=15],你应该有 begin()
和朋友的 const
和非 const
重载=]访问。
有一个标准描述了您的 class 接口应该是什么样子,如果您希望它们与 STL 一致的话。 C++ 有这个 'concepts' 的概念,它确定了给定 class 的要求是一个概念的充分实现。这几乎成了c++11的一个语言特性。
您可能感兴趣的一个概念是 Container 概念。如您所见,为了满足容器概念的要求,您需要 begin
、cbegin
、end
和 cend
作为成员函数(除其他外) .
由于您似乎将数据存储在数组中,因此您可能也对 SequenceContainer.
感兴趣
我建议创建两组函数——成员函数和非成员函数——以实现最大的灵活性。
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
和 [= 的参数,则成员函数是必需的16=].
如果您希望能够使用类型为 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 const&)、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 了解有关制作有效迭代器所需内容的更多信息。 (注意:您还需要某些属性是有效的 "container",例如 .size())
理想情况下,您应该为开始和结束使用成员函数,但这不是必需的...您也可以重载 std::begin 和 std::end。如果您不知道该怎么做,我建议您使用成员函数。
您应该创建 begin() const 和 end() const,但它应该是 cbegin() 的别名,绝不与 begin() 相同!
对于class如
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();
}
还有其他需要考虑的事情吗?是否有 tools/techniques 来实现 "easy to get right" 并减少样板代码的数量?
一些相关的questions/discussion包括:
- Should custom containers have free begin/end functions?
- Why use non-member begin and end functions in C++11?
- When to use
std::begin
andstd::end
instead of container specific versions
我选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
和非 [=15],你应该有 begin()
和朋友的 const
和非 const
重载=]访问。
有一个标准描述了您的 class 接口应该是什么样子,如果您希望它们与 STL 一致的话。 C++ 有这个 'concepts' 的概念,它确定了给定 class 的要求是一个概念的充分实现。这几乎成了c++11的一个语言特性。
您可能感兴趣的一个概念是 Container 概念。如您所见,为了满足容器概念的要求,您需要 begin
、cbegin
、end
和 cend
作为成员函数(除其他外) .
由于您似乎将数据存储在数组中,因此您可能也对 SequenceContainer.
感兴趣我建议创建两组函数——成员函数和非成员函数——以实现最大的灵活性。
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
和 [= 的参数,则成员函数是必需的16=].
如果您希望能够使用类型为 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 const&)、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 了解有关制作有效迭代器所需内容的更多信息。 (注意:您还需要某些属性是有效的 "container",例如 .size())
理想情况下,您应该为开始和结束使用成员函数,但这不是必需的...您也可以重载 std::begin 和 std::end。如果您不知道该怎么做,我建议您使用成员函数。
您应该创建 begin() const 和 end() const,但它应该是 cbegin() 的别名,绝不与 begin() 相同!