如何在C++中编写一个函数,以容器不可知的方式将特定类型的迭代器作为参数?即
// C# version
void foo(IEnumerable<MyConcreteClass> t)
{
foreach(MyConcreteClass c in t)
{
c.MyFunction();
}
}
读到迭代器,我似乎应该做这样的事情:
template<MyIter>
void foo(MyIter start, MyIter end, std::input_iterator_tag type)
{
// How would the next part work? Do I do:
while (start != end)
{
MyConcreteClass* c = *start; // this will compile iff the parameter is correct.
c->MyFunction();
start++;
}
在传入无效迭代器类型(例如std::unordered_set<MyOtherClass*>::iterator
)的情况下,我觉得使用此方法生成的编译器错误只是在我取消引用start
的行处的某种无效强制转换错误;相反,我希望得到一个错误,我实际上是在传递错误类型的迭代器。有更好的方法吗?我很乐意说"MyIter必须是MyConcreteClass容器中的迭代器"
BTW、C++11机制还可以。
如果你愿意,你可以添加一些类似的东西
static_assert(std::is_convertible<decltype(*start), MyConcreteClass*>::value, "MyIter must be an iterator from a container of MyConcreteClass");
到函数体内部的任何位置。
请注意,这将MyConcreteClass
和foo
紧密耦合在一起,所以如果您确定要这样做,那么也没关系。但如果你不这样做,你可以将其更多地通用化并编写
template<MyIter>
void foo(MyIter start, MyIter end)
{
while (start != end) {
auto c = *start;
c->MyFunction();
// Or instead of the above two statements:
// (*c)->MyFunction();
start++;
}
}
这将适用于指向任何具有MyFunction
函数的类型的指针,因此现在您甚至可以使用unique_ptr
的容器,而以前不能。但是,您不能很容易地为这个编写断言语句,所以这是一个折衷。C++的传统只是记录函数(例如,"此函数适用于在operator->
后面有MyFunction()
可用的所有类型"),如果违反要求,则让编译器出错。
对于您的函数,您可以使用以下内容:
template <template <typename...> class Container>
void foo(Container<MyConcreteClass>& container)
{
for (auto elem : container)
{
elem.MyFunction();
}
}
之后的额外参数的更完整示例:您可以在之前对参数使用类似的工作
template <template <typename...> class Container, typename... Ts>
void foo(Container<MyConcreteClass, Ts...>& container)
{
for (auto elem : container)
{
elem.MyFunction();
}
}
如果同时使用(before和after),则可能会被视为不明确(我的gcc无法编译)。