Reputation: 634
I'm trying to write a function like std::for_each
, that in addition to the normal usage, can also take a std::function<bool (param)>
. A false return value means that I want to break out of the loop. The code below is what I've gotten so far.
The second call to a.visit([&](int) -> void)
does not compile when evaluating !visitor(i). Is it possible to make this work or am I barking up the wrong tree?
I'm using MSVC 2010 but want the code to be generally C++11 compatible.
#include <list>
#include <string>
#include <iostream>
struct A
{
std::list<int> _lst;
template<typename _F>
void visit(_F visitor)
{
for(std::list<int>::const_iterator it = _lst.begin(), end = _lst.end() ; it != end ; it++) {
int i = *it;
if (std::is_void<decltype(visitor(i))>::value) {
visitor(i);
} else {
if (!visitor(i)) { // <----- error C2171: '!' : illegal on operands of type 'void'
break;
}
}
}
}
};
int main(int argc, char* argv[])
{
A a;
// populate a
for (int i = 0 ; i < 10 ; i++) {
a._lst.push_back(i);
}
a.visit([](int i) -> bool {
std::cout << i << std::endl;
return i < 5;
});
a.visit([](int i) {
std::cout << i << std::endl;
});
}
Upvotes: 6
Views: 935
Reputation: 4527
Here's how I would implement for_almost_each
; I'm using namespace std
plus type aliases for readability purposes.
#include <algorithm>
#include <iterator>
#include <functional>
using namespace std;
template<class Iter, class Func>
Iter
for_almost_each_impl(Iter begin, Iter end, Func func, std::true_type)
{
for (auto i = begin; i!=end; ++i)
if (!func(*i))
return i;
return end;
}
template<class Iter, class Func>
Iter
for_almost_each_impl(Iter begin, Iter end, Func func, std::false_type)
{
for_each(begin, end, func);
return end;
}
template<class Iter, class Func>
Iter for_almost_each(Iter begin, Iter end, Func func)
{
using Val = typename iterator_traits<Iter>::value_type;
using Res = typename result_of<Func(Val)>::type;
return for_almost_each_impl(begin, end,
func,
is_convertible<Res, bool>{} );
}
I used is_convertible
, as it seems to make more sense than is_same
.
Upvotes: 6
Reputation: 7112
Your std::is_void needs to be done at compile time and can't be done inside the function body. This use of function overloading will work:
#include <list>
#include <string>
#include <iostream>
#include <type_traits> // missing header
struct A
{
std::list<int> _lst;
// wrapper for bool returning visitor
template<typename _F, typename Iter>
bool do_visit(_F visitor, Iter it, std::true_type)
{
return visitor(*it);
}
// wrapper for non-bool returning visitor
template<typename _F, typename Iter>
bool do_visit(_F visitor, Iter it, std::false_type)
{
visitor(*it);
return true;
}
template<typename _F>
void visit(_F visitor)
{
for (auto it = _lst.begin(), end = _lst.end() ; it != end ; it++) {
// select correct visitor wrapper function using overloading
if (!do_visit(visitor, it, std::is_same<bool, decltype(visitor(0))>())) {
break;
}
}
}
};
int main(int argc, char* argv[])
{
A a;
// populate a
for (int i = 0 ; i < 10 ; i++) {
a._lst.push_back(i);
}
a.visit([](int i) -> bool {
std::cout << i << std::endl;
return i < 5;
});
a.visit([](int i) {
std::cout << i << std::endl;
});
}
Upvotes: 4
Reputation: 880
This lambda doesn't return a value, which is why you're getting an error that "visitor" is returning void:
a.visit([](int i) {
std::cout << i << std::endl;
});
You could make this work by rewriting as:
a.visit([](int i) -> bool {
std::cout << i << std::endl;
return true;
});
Upvotes: -1