We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.
Nice! Fully working example for reference: https://wandbox.org/permlin...
1. `std::size_t` rather than `size_t`.
2. Actually, perhaps you should use `typename T::size_type` instead of `std::size_t`
Using size_type won't work with plain C arrays, though.
std::iterator_traitshttps://en.cppreference.com...
Lovely. Have you considered/benchmarked keeping "begin" instead of i and do pointer math?
If you assume pointer math, then it won't work with arbitrary containers, unfortunately.
awsome !
Quite good. A few things that strike me as odd:
iterable_wrapper::iterable should be a reference, otherwise there will be a copy.
The enumerate function itself should probably have a reference argument, since we are not really "moving" anything.
Both of the things you mention are there so you can apply enumerate() to an rvalue, say an iterator returned by another function—in which case it will move that rvalue into iterable_wrapper::iterable, extending its lifetime, which wouldn't happen if it were declared as an lvalue reference. (And if enumerate() is applied to an lvalue, T will deduce to a lvalue reference, so there's no copying in that case.)
Aah I see, didn't know that T && will deduce to T& when an lvalue is passed. Learned something new today!
Nice, been looking for something like this. But I have a question, why is the following line there?
typename = decltype(std::end(std::declval<t>()))
I tried removing it and it still seems to work. So what is it doing?
Magnus, that line is there to ensure the function will fail to match (in the SFINAE sense) if the type it's applied to is not fully iterable. Both std::begin and std::end should be applicable to the thing you're trying to enumerate.
Strictly speaking, we don't need either of those checks in the template declaration (we could define TIter as a typedef in the body of the function). However, putting them in the template as SFINAE clauses gives nicer error messages if the function is applied to something non-iterable. You'll get something like "couldn't find an overload of enumerate() to match this". Without those clauses, you'd instead get some gobbledygook errors from the innards of the function where it tries to use std::begin and std::end.
Ok, thank you very much for the explanation.
The SFINAE error message readability depends somewhat on the compiler. Alternatively, you can remove the SFINAE clauses from the function template signature and put a static_assert:static_assert(std::is_same_v< decltype(std::begin(std::declval<t>())), decltype(std::end(std::declval<t>())) >);
which will be output explicitly in case of violation. The additional benefit is that this closes the door for another templated enumerate overload that requires the exact opposite of your SFINAE clauses.
Here's the same thing done with ranges from C++20: https://godbolt.org/z/yS2AkG . Completely avoids
SFINAE. C++20 is not final yet so things might change a bit, but I believe this code shouldn't be affected.
As it happens I was just experimenting with some C++20 metaprogramming this weekend. Indeed it's nice to swap SFINAE for concepts and requires-clauses. In fact you don't need ranges for this (I'm not sure they really add a lot here), you can express the constraint with a requires-clause.
Hi! This is a very useful class. I'm a maintainer on a relatively big open source music project (surge) and would like to use this in a MIT licensed set of utility classes we have across our products (perhaps with modifications). I would of course give credit, but thought I would ask as well. Thanks!
Sorry I did not see this when it was posted, but that's fine. There's an Apache licensed version of this on my github: https://github.com/Reedbeta...
Doesn't seem to work correctly when T is a pointer to a class type (as opposed to a class itself), on VS2020. Maybe due to *iter dereferencing the wrong thing?
I'm not sure what you mean. Like T is a pointer to a std::vector for example? Of course you would need to dereference it when you pass it to enumerate() - just as you would if you used it in a range-for loop for example.
Ah sorry, no I meant if you want to enumate a `vector<foo *="">` for example.
Seems like it works fine to me: https://gcc.godbolt.org/z/a...
If you iterate a vector of pointers, then the items you get back are the pointers, naturally.
Good to know it works now.. my original comment was from 2 years ago, not sure what VS I was on at the time..
Hello,
A little bit late in the battle, but thanks for sharing this class.
I have just a remark: what if one tries something like 'enumerate(enumerate(string("hello")))' ? It seems it doesn't compile since the inner iteration provides a rvalue, which can't be bind to the lvalue reference of the outer iteration through a 'tie'
As this, this example doesn't seem very useful but the issue could rise if the inner iteration provides temporary objects. But it is quite possible that I missed some point.
A possibility would be to make operator* returning 'std::make_tuple(i,*iter)' but I guess that we would have a (potentially costly) copy of the '*iter' object, which is far from ideal.
Hi, can you explain why you define struct inside function?
Hi cppDev, it's because the struct is really an internal implementation detail and I prefer not to expose it outside the function.
Thanks for sharing this. Could you clarify the phrase " I present it here for your use" above your code. Are you releasing your code into the public domain or else under what terms do you permit the use of your code?
Hi Manfred, see the github repository for this: https://github.com/Reedbeta... which is under Apache2 license.
This is awesome! :D Very cool indeed, thanks for sharing :)