比较简单的自定义容器签名不满足<ranges>概念
Relatively simple custom container signature does not fulfill <ranges> concept
我正在尝试使用 <ranges>
(C++20 MSVC) 迭代名为 Span
的自定义容器,其 begin/end()
方法 return a SpanIter
.但是,以下静态断言在 test()
方法中失败,并且我的 class(但不是 vector<>
)无法识别管道运算符。
static_assert(std::ranges::range<Span>, "Is not a range");
我认为方法签名匹配 this working example。我无法找到丢失的内容,无论是方法还是 typedef/using.
#include <ranges>
#include <iostream>
#include <vector>
namespace ptest {
class SpanIter;
struct Span;
struct Span : std::ranges::view_base
{
using iterator = SpanIter;
using sentinel = SpanIter;
int start;
int length;
int index;
Span() noexcept { start = length = index = 0; }
Span(int s, int l, int i) noexcept : start(l), length(l), index(i) { }
Span(const Span& other) noexcept : Span(other.start, other.length, other.index) { }
Span(const Span&& other) noexcept : Span(other.start, other.length, other.index) { }
Span& operator=(const Span& other) { start = other.start; length = other.length; index = other.index; }
bool operator==(const Span& other) const { return start == other.start && length == other.length && index == other.index; }
iterator begin();
sentinel end();
iterator cbegin() const;
sentinel cend() const;
size_t size() { return length; }
void Print() {
// eg. "range[5 - 9]: 5"
std::cout << "range [" << start << "-"
<< (start + length - 1)
<< "]: " << index << std::endl;
}
auto operator <=> (const Span& other) const = default;
};
class SpanIter {
Span Empty;
using value_type = Span;
using reference = Span&;
using const_reference = const Span&;
using iterator = SpanIter;
using sentinel = SpanIter;
using const_iterator = const SpanIter;
using iterator_category = std::forward_iterator_tag;
using difference_type = ptrdiff_t;
using pointer = Span*;
using size_type = size_t;
public:
SpanIter() noexcept { }
SpanIter(const SpanIter& other) noexcept : current_(other.current_) { }
SpanIter(const SpanIter&& other) noexcept : current_(other.current_) { }
SpanIter(Span t) noexcept : current_(t) {
current_.index = current_.start;
}
~SpanIter() { }
SpanIter& operator++() {
if (current_.index < current_.start + current_.length)
++current_.index;
return *this;
}
SpanIter operator++(int) {
SpanIter result = *this;
++(*this);
return result;
}
SpanIter& operator = (const SpanIter& other)
{
current_ = other.current_;
return *this;
}
bool operator==(const SpanIter& other) const {
return this->operator*() == *other;
}
bool operator!=(const SpanIter& other) const {
return !(*this == other);
}
template<typename T>
bool operator==(T&& value) const {
return this->operator*() == std::forward<T>(value);
}
template<typename T>
bool operator!=(T&& value) const {
return !(*this == value);
}
Span& operator*() {
return current_.start <= current_.index &&
current_.index < current_.start + current_.length
? current_
: Empty;
}
const Span& operator*() const {
return current_.start <= current_.index &&
current_.index < current_.start + current_.length
? current_
: Empty;
}
private:
Span current_;
};
SpanIter Span::begin() { return SpanIter(*this); }
Span::sentinel Span::end() { return SpanIter(Span()); }
SpanIter Span::cbegin() const { return SpanIter(*this); }
Span::sentinel Span::cend() const { return SpanIter(Span()); }
SpanIter begin(const Span& span) { return span.cbegin(); }
Span::sentinel end(const Span& span) { return span.cbegin(); }
void test() {
// WORKS (confirm pipes work with vector)
// output: 5 6
std::vector<int> v { 5, 6, 7, 8, 9};
auto works = v | std::views::take(2);
for (int i : works)
std::cout << i << " ";
// FAILS !!!!!!!!!!!!!!!!!!!
// <ranges> doesn't recognize my Span container as a range
static_assert(std::ranges::range<Span>, "Is not a range");
// FAILS !!!!!!!!!!!!!!!!!!!
// binary '|': 'Span' does not define this operator or a conversion
// to a type acceptable to the predefined operator
Span span(6, 5, 8);
auto fails = span | std::views::take(2);
// WORKS: confirm my Span operates as a container with normal C++ 11 features
// output:
// range[5 - 9] : 5
// range[5 - 9] : 6
// range[5 - 9] : 7
// range[5 - 9] : 8
// range[5 - 9] : 9
for (Span s : span)
s.Print();
}
}
问题是迭代器特征的 using
语句需要 public ... 就是这样。这对于大多数熟悉 C++ 的人来说肯定是显而易见的,但可能会帮助像我这样的其他相关新手。
我正在尝试使用 <ranges>
(C++20 MSVC) 迭代名为 Span
的自定义容器,其 begin/end()
方法 return a SpanIter
.但是,以下静态断言在 test()
方法中失败,并且我的 class(但不是 vector<>
)无法识别管道运算符。
static_assert(std::ranges::range<Span>, "Is not a range");
我认为方法签名匹配 this working example。我无法找到丢失的内容,无论是方法还是 typedef/using.
#include <ranges>
#include <iostream>
#include <vector>
namespace ptest {
class SpanIter;
struct Span;
struct Span : std::ranges::view_base
{
using iterator = SpanIter;
using sentinel = SpanIter;
int start;
int length;
int index;
Span() noexcept { start = length = index = 0; }
Span(int s, int l, int i) noexcept : start(l), length(l), index(i) { }
Span(const Span& other) noexcept : Span(other.start, other.length, other.index) { }
Span(const Span&& other) noexcept : Span(other.start, other.length, other.index) { }
Span& operator=(const Span& other) { start = other.start; length = other.length; index = other.index; }
bool operator==(const Span& other) const { return start == other.start && length == other.length && index == other.index; }
iterator begin();
sentinel end();
iterator cbegin() const;
sentinel cend() const;
size_t size() { return length; }
void Print() {
// eg. "range[5 - 9]: 5"
std::cout << "range [" << start << "-"
<< (start + length - 1)
<< "]: " << index << std::endl;
}
auto operator <=> (const Span& other) const = default;
};
class SpanIter {
Span Empty;
using value_type = Span;
using reference = Span&;
using const_reference = const Span&;
using iterator = SpanIter;
using sentinel = SpanIter;
using const_iterator = const SpanIter;
using iterator_category = std::forward_iterator_tag;
using difference_type = ptrdiff_t;
using pointer = Span*;
using size_type = size_t;
public:
SpanIter() noexcept { }
SpanIter(const SpanIter& other) noexcept : current_(other.current_) { }
SpanIter(const SpanIter&& other) noexcept : current_(other.current_) { }
SpanIter(Span t) noexcept : current_(t) {
current_.index = current_.start;
}
~SpanIter() { }
SpanIter& operator++() {
if (current_.index < current_.start + current_.length)
++current_.index;
return *this;
}
SpanIter operator++(int) {
SpanIter result = *this;
++(*this);
return result;
}
SpanIter& operator = (const SpanIter& other)
{
current_ = other.current_;
return *this;
}
bool operator==(const SpanIter& other) const {
return this->operator*() == *other;
}
bool operator!=(const SpanIter& other) const {
return !(*this == other);
}
template<typename T>
bool operator==(T&& value) const {
return this->operator*() == std::forward<T>(value);
}
template<typename T>
bool operator!=(T&& value) const {
return !(*this == value);
}
Span& operator*() {
return current_.start <= current_.index &&
current_.index < current_.start + current_.length
? current_
: Empty;
}
const Span& operator*() const {
return current_.start <= current_.index &&
current_.index < current_.start + current_.length
? current_
: Empty;
}
private:
Span current_;
};
SpanIter Span::begin() { return SpanIter(*this); }
Span::sentinel Span::end() { return SpanIter(Span()); }
SpanIter Span::cbegin() const { return SpanIter(*this); }
Span::sentinel Span::cend() const { return SpanIter(Span()); }
SpanIter begin(const Span& span) { return span.cbegin(); }
Span::sentinel end(const Span& span) { return span.cbegin(); }
void test() {
// WORKS (confirm pipes work with vector)
// output: 5 6
std::vector<int> v { 5, 6, 7, 8, 9};
auto works = v | std::views::take(2);
for (int i : works)
std::cout << i << " ";
// FAILS !!!!!!!!!!!!!!!!!!!
// <ranges> doesn't recognize my Span container as a range
static_assert(std::ranges::range<Span>, "Is not a range");
// FAILS !!!!!!!!!!!!!!!!!!!
// binary '|': 'Span' does not define this operator or a conversion
// to a type acceptable to the predefined operator
Span span(6, 5, 8);
auto fails = span | std::views::take(2);
// WORKS: confirm my Span operates as a container with normal C++ 11 features
// output:
// range[5 - 9] : 5
// range[5 - 9] : 6
// range[5 - 9] : 7
// range[5 - 9] : 8
// range[5 - 9] : 9
for (Span s : span)
s.Print();
}
}
问题是迭代器特征的 using
语句需要 public ... 就是这样。这对于大多数熟悉 C++ 的人来说肯定是显而易见的,但可能会帮助像我这样的其他相关新手。