C++11 如何允许派生类通过虚方法返回类型上的任何迭代器?
我正在尝试编写一个小库,它允许用户实现某些行为,以便该库可以使用自己的底层数据结构。在这种特殊情况下(尽管我正在简化),我试图定义如下内容:C++11 如何允许派生类通过虚方法返回类型上的任何迭代器?,c++11,templates,iterator,return-value,virtual-functions,C++11,Templates,Iterator,Return Value,Virtual Functions,我正在尝试编写一个小库,它允许用户实现某些行为,以便该库可以使用自己的底层数据结构。在这种特殊情况下(尽管我正在简化),我试图定义如下内容: class-LibraryClass{ 虚拟std::vector::const_迭代器intsBegin()const=0; 虚拟std::vector::const\u迭代器intsEnd()const=0; }; 换句话说,该库将接受使用任何派生类,从而允许它在整数向量上进行迭代。现在棘手的一点是:我不想强制使用向量。实际上,对我来说,任何对整数的
class-LibraryClass{
虚拟std::vector::const_迭代器intsBegin()const=0;
虚拟std::vector::const\u迭代器intsEnd()const=0;
};
换句话说,该库将接受使用任何派生类,从而允许它在整数向量上进行迭代。现在棘手的一点是:我不想强制使用向量。实际上,对我来说,任何对整数的迭代器都是可以的,只要我能检测到它何时结束,并将其解引用为整数。以下是缺失的部分:
class-LibraryClass{
虚拟/*???*/intsBegin()常量=0;
虚拟/*???*/intsEnd()常量=0;
};
类用户类{
/*这应该是允许的,这里我用vector作为例子*/
std::vector::const_迭代器intsBegin()const;
std::vector::const_迭代器intsEnd()const;
};
现在,我知道如何通过模板获取方法参数,但我找不到在虚拟方法中将其作为返回值的方法,因为:
- 迭代器是否为常量并不重要
- 基类中不会有任何默认行为
- 该机制不应受到继承的干扰:从
迭代器派生的类也应被接受为返回类型,因为它们满足要求。我不认为这是一个问题,因为及物性,但仍然是int
- 理想情况下,应该强制
和begin
方法在给定的派生类中具有相同的迭代器类型end
- 更理想的情况是,我希望避免将Boost用于
。该库是使用C++11编写的(至少我尝试过)enable\u if
class LibraryClass {
public:
virtual void visitInts(std::function<void(int)> f) = 0;
};
class UserClass : public LibraryClass {
public:
void visitInts(std::function<void(int)> f) override {
for (int data : m_vec)
f(data);
}
private:
std::vector<int> m_vec;
};
class-LibraryClass{
公众:
虚空访问(标准::函数f)=0;
};
类用户类:公共图书馆类{
公众:
无效访问(标准::函数f)覆盖{
用于(整数数据:m_vec)
f(数据);
}
私人:
std::向量m_-vec;
};
例如,您失去了
std::binary_search
的功能,但您基本上具有相同的多功能性,无需在用户类上强制使用特定的存储机制,您也可以将迭代器设置为虚拟类,例如(未经测试,并且明显不完整):
class-LibraryClass{
受保护的:
struct IteratorBase{//用户需要实现以下功能:
虚拟常量int&deref()常量=0;
虚空增量()=0;
虚空减量()=0;
//进一步的方法,例如比较和可能的克隆
};
虚拟std::unique_ptr intsBegin_uz()常量=0;
虚拟std::unique_ptr intsEnd_uz()常量=0;
公众:
类迭代器{
朋友班图书馆班;
标准:独特的国际热核聚变实验堆;
迭代器(std::unique_ptr&&iter):iter(std::move(iter){}
公众:
常量int&运算符*(){return iter->deref();}
常量迭代器和运算符++(){iter->increment();返回*this;}
//其他操作员。。。
};
迭代器intsBegin()常量{返回迭代器(intsBegin_());}
迭代器intsEnd()常量{返回迭代器(intsEnd();}
};
我不确定这是否值得付出努力,特别是因为即使对于非常简单的任务,也会有很多间接操作。我想你应该重新考虑一下你的设计…任何迭代器都不难编写。如果你关心性能,这通常是个坏主意,因为每个操作都需要非零开销来完成类型ERA乌尔
假设您只需要为(:)
循环和“输入”迭代支持
namespace any_iteration_support {
template<class VTable, class...Ts>
VTable create() {
VTable retval;
VTable::template populate<Ts...>(retval);
return retval;
}
template<class VTable, class...Ts>
VTable const* get_vtable() {
static VTable vtable = create<VTable, Ts...>();
return &vtable;
}
struct input_iterator_vtable {
void(*next)(void*) = 0;
void(*dtor)(void*) = 0;
void(*copy)(void* dest, void const* src) = 0;
void*(*clone)(void const* src) = 0;
bool(*equals)(void const* lhs, void const* rhs) = 0;
template<class It>
static void populate( input_iterator_vtable& vtable ) {
vtable.next = [](void* ptr){
auto& it = *static_cast<It*>(ptr);
++it;
};
vtable.dtor= [](void* ptr){
auto* pit = static_cast<It*>(ptr);
delete pit;
};
vtable.copy= [](void* dest, void const* src){
auto& destit = *static_cast<It*>(dest);
auto const& srcit = *static_cast<It const*>(src);
destit = srcit;
};
vtable.clone= [](void const* src)->void*{
auto const& srcit = *static_cast<It const*>(src);
return new It(srcit);
};
vtable.equals= [](void const* lhs, void const* rhs)->bool{
auto const& lhsit = *static_cast<It const*>(lhs);
auto const& rhsit = *static_cast<It const*>(rhs);
return lhsit == rhsit;
};
}
};
template<class T>
struct any_input_iterator_vtable:input_iterator_vtable {
T(*get)(void*) = 0;
template<class It>
static void populate(any_input_iterator_vtable& vtable) {
input_iterator_vtable::populate<It>(vtable);
vtable.get = [](void* ptr)->T{
auto& it = *static_cast<It*>(ptr);
return *it;
};
}
};
}
template<class T>
struct any_input_iterator {
any_iteration_support::any_input_iterator_vtable<T> const* vtable = 0;
void* state = 0;
template<class It,
std::enable_if_t<!std::is_same<It, any_input_iterator>::value, int> =0
>
any_input_iterator( It it ):
vtable(any_iteration_support::get_vtable<any_iteration_support::any_input_iterator_vtable<T>, It>()),
state( new It(std::move(it)) )
{}
any_input_iterator(any_input_iterator&& o):
vtable(o.vtable),
state(o.state)
{
o.vtable = 0; o.state = 0;
}
any_input_iterator& operator=(any_input_iterator&& o) {
using std::swap;
swap(vtable, o.vtable);
swap(state, o.state);
return *this;
}
any_input_iterator(any_input_iterator const& o) {
if (!o.vtable) return;
state = o.vtable->clone(o.state);
vtable = o.vtable;
}
any_input_iterator& operator=(any_input_iterator const& o) {
if (!vtable && !o.vtable) return *this;
if (vtable == o.vtable) {
vtable->copy( state, o.state );
return *this;
}
clear();
if (!o.vtable) {
return *this;
}
state = o.vtable->clone(o.state);
vtable = o.vtable;
return *this;
}
explicit operator bool()const { return vtable; }
friend bool operator==(any_input_iterator const& lhs, any_input_iterator const& rhs) {
if (!lhs && !rhs) return true;
if (!lhs) return false;
if (lhs.vtable != rhs.vtable) return false;
return lhs.vtable->equals( lhs.state, rhs.state );
}
friend bool operator!=(any_input_iterator const& lhs, any_input_iterator const& rhs) {
return !(lhs==rhs);
}
T operator*() const {
return vtable->get( state );
}
any_input_iterator& operator++() {
vtable->next(state);
return *this;
}
any_input_iterator operator++(int) {
auto retval = *this;
++*this;
return retval;
}
~any_input_iterator() { clear(); }
void clear() {
if (!vtable) return;
auto dtor = vtable->dtor;
auto s = state;
state = 0;
vtable = 0;
dtor(s);
}
using reference = T;
using value_type = typename std::remove_reference<T>::type;
using iterator_category = std::input_iterator_tag;
};
当然,手动执行这种类型的擦除是乏味且容易出错的。我会使用类似类型擦除的方法,而不是使用上面提到的方法
有效的方法是使用span访问者
template<class T>
struct span {
T* b = 0; T* e = 0;
T* begin() const { return b; }
T* end() const { return e; }
bool empty() const { return begin()==end(); }
std::size_t size() const { return end()-begin(); }
span()=default;
span(span const&)=default;
span& operator=(span const&)=default;
span(T* s, T* f):b(s),e(f) {};
span(T* s, std::size_t length):span(s, s+length) {}
};
template<class T>
using span_visitor = std::function<void(span<T>)>;
class LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const = 0;
};
class VecImpl : public LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const override {
f( {m_vec.data(), m_vec.size()} );
};
private:
std::vector<int> m_vec;
};
class SimpleListImpl : public LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const override {
for (int i : m_list )
f( {&i, 1} );
};
private:
std::list<int> m_list;
};
class FancyListImpl : public LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const override {
std::size_t count = 0;
std::array<int, 10> buffer;
for (int i : m_list ) {
buffer[count] = i;
++count;
if (count == buffer.size()) {
f({ buffer.data(), buffer.size()});
count = 0;
}
}
if (count) f({ buffer.data(), count});
};
private:
std::list<int> m_list;
};
模板
结构跨度{
T*b=0;T*e=0;
T*begin()常量{return b;}
T*end()常量{return e;}
bool empty()常量{return begin()==end();}
std::size_t size()常量{return end()-begin();}
span()=默认值;
span(span const&)=默认值;
span&运算符=(span常量&)=默认值;
span(T*s,T*f):b(s),e(f){};
span(T*s,std::size_T length):span(s,s+length){}
};
模板
使用span_visitor=std::函数;
班级图书馆班级{
公众:
虚拟无效访问量(span_访问者f)常数=0;
};
类VecImpl:公共图书馆类{
公众:
虚拟无效访问(span_visitor f)常量覆盖{
f({m_vec.data(),m_vec.size()});
};
私人:
std::向量m_-vec;
};
类SimpleListImpl:公共图书馆类{
公众:
虚拟无效访问(span_visitor f)常量覆盖{
用于(int i:m_列表)
f({&i,1});
};
私人:
std::列表m_列表;
};
类FancyListImpl:公共图书馆类{
公众:
虚拟无效访问(span_visitor f)常量覆盖{
标准::大小\u t计数=0;
std::数组缓冲区;
用于(int i:m_列表){
缓冲区[计数]=i;
++计数;
if(count==buffer.size()){
f({buffer.data(),buffer.size()});
计数=0;
}
}
if(count)f({buffer.data(),count});
};
私人:
std::列表m_列表;
};
你怎么吃
std::vector<int> get_ints_from(LibraryClass const& library) {
std::vector<int> vec;
library.visitInits([&](span<int const> ints){
vec.append(ints.begin(), ints.end());
});
return vec;
}
std::vector get_ints_from(LibraryClass常量和库){
std::vec;
库访问([&](span ints){
append(ints.begin(),ints.end());
});
返回向量;
}
此接口将类型擦除移动到每个元素多次到每个元素一次
使用这种方法,您可以获得接近直接公开容器的性能,b
template<class T>
struct span {
T* b = 0; T* e = 0;
T* begin() const { return b; }
T* end() const { return e; }
bool empty() const { return begin()==end(); }
std::size_t size() const { return end()-begin(); }
span()=default;
span(span const&)=default;
span& operator=(span const&)=default;
span(T* s, T* f):b(s),e(f) {};
span(T* s, std::size_t length):span(s, s+length) {}
};
template<class T>
using span_visitor = std::function<void(span<T>)>;
class LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const = 0;
};
class VecImpl : public LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const override {
f( {m_vec.data(), m_vec.size()} );
};
private:
std::vector<int> m_vec;
};
class SimpleListImpl : public LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const override {
for (int i : m_list )
f( {&i, 1} );
};
private:
std::list<int> m_list;
};
class FancyListImpl : public LibraryClass {
public:
virtual void visitInts(span_visitor<int const> f) const override {
std::size_t count = 0;
std::array<int, 10> buffer;
for (int i : m_list ) {
buffer[count] = i;
++count;
if (count == buffer.size()) {
f({ buffer.data(), buffer.size()});
count = 0;
}
}
if (count) f({ buffer.data(), count});
};
private:
std::list<int> m_list;
};
std::vector<int> get_ints_from(LibraryClass const& library) {
std::vector<int> vec;
library.visitInits([&](span<int const> ints){
vec.append(ints.begin(), ints.end());
});
return vec;
}