如何在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
),我觉得使用此方法生成的编译器错误只是在我解除引用的行上的某种无效的转换错误{ {1}};我想反而得到一个错误,我实际上是在一个错误类型的迭代器中传递的。有一个更好的方法吗?我很高兴地说“MyIter必须是来自MyConcreteClass容器的迭代器”
BTW,C ++ 11机制都可以。
答案 0 :(得分:3)
如果需要,可以添加类似
的内容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
s的容器,而你之前就不能这样。但是,你不能轻易地为这个写出断言声明,所以这是一个权衡。 C ++的传统只是记录函数(例如“此函数适用于MyFunction()
后面有operator->
可用的所有类型”,并且如果编译器违反了要求,就会出错。
答案 1 :(得分:2)
对于您的功能,您可以使用以下内容:
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();
}
}
如果你同时使用(之前和之后),它可能被视为含糊不清(我的gcc无法编译)。