Re: Where are iterators for rvalue containers?
Am 14.03.2012 22:31, schrieb Gene Bushuyev:
On Mar 2, 1:42 pm, Daniel Kr?gler<daniel.krueg...@googlemail.com>
wrote:
On 2012-03-02 11:41, Gene Bushuyev wrote:
I was looking at the C++11 standard docs and didn't see iterators for
rvalue containers.
...
The standard did not miss to add them. It would be possible to follow
your suggestion with the cost of
a) replacing all container begin/end pairs by a double-pair each one
&-ref-qualified and&&-ref-qualified
b) doubling the number of iterators with in each container:
&-ref-qualified begin/end functions return the normal one,
&&-ref-qualified returning a moving iterator instead.
Instead the standard chose a more economic and useful feature:
std::move_iterator. Just replace your code above by
MyContainer::MyContainer(vector<Object>&& v)
{
reserve(v.size());
uninitialized_copy(std::make_move_iterator(v.begin()),
std::make_move_iterator(v.end()), data);
size = v.size();
}
Thank you for pointing move_iterator template to me, but it doesn't
look like a better solution.
That depends on the weights used for the measure. make_move_iterator
solves the problem and *not* adding all these overloads plus one further
iterator type is much easier to learn or to teach. It is also much
easier to specifier. I consider all these arguments as relevant.
I would rather see all containers having
defined all functions:
using iterator =<implementation_defined>
using const_iterator =<implementation_defined>
using move_iterator =<implementation_defined>
iterator begin()& ;
const_iterator begin() const&;
move_iterator begin()&&;
As I said before, this would have considerably increased the number of
all iterator-returning functions and it would not make C++03 containers
move-aware via the iterator. Further, the story has not yet ended:
(r)(c)begin()/(r)(c)end() are not the only iterator-returning functions,
just look for all remaining iterator-returning functions in the string
clause or the container clause. In addition, any user-code that wants to
create a C++11 container has to create all the boiler-code to satisfy
the requirements. IMO two instead of three function for each such
iterator function is more than enough!
Please refer to some real arguments, because from what you write so far
I really cannot deduce the overall advantage of such a change.
Because, it might be unknown, whether a container comes as an lvalue
or rvalue, and thus one need a duck-typing way of addressing it, for
example:
template<class V>
auto unique(V&& v) -> set<typename
remove_reference<V>::type::value_type>
{
return set<typename remove_reference<V>::type::value_type>
(forward<V>(v).begin(), forward<V>(v).end());
}
If this pattern occurs often for you, it could be useful to introduce
once and for all a helper function that maps rvalue containers to a
moving iterator range, else a normal iterator range. This is easy to
realize, e.g.
#include <type_traits>
#include <iterator>
#include <utility>
template<class C>
inline auto make_iterator_range(C&& c) ->
typename std::enable_if<std::is_lvalue_reference<C>::value,
std::pair<decltype(c.begin()), decltype(c.end())>
::type
{
return {c.begin(), c.end()};
}
template<class C>
inline auto make_iterator_range(C&& c) ->
typename std::enable_if<!std::is_lvalue_reference<C>::value,
std::pair<
decltype(std::make_move_iterator(c.begin())),
decltype(std::make_move_iterator(c.end()))
>
::type
{
return {std::make_move_iterator(c.begin()),
std::make_move_iterator(c.end())};
}
#include <vector>
int main() {
using C = std::vector<int>;
C c1{};
auto r1 = make_iterator_range(c1);
static_assert(std::is_same<decltype(r1), std::pair<C::iterator,
C::iterator>>::value, "");
const C c2{};
auto r2 = make_iterator_range(c2);
static_assert(std::is_same<decltype(r2),
std::pair<C::const_iterator, C::const_iterator>>::value, "");
auto r3 = make_iterator_range(C{});
static_assert(std::is_same<decltype(r3),
std::pair<std::move_iterator<C::iterator>,
std::move_iterator<C::iterator>>>::value, "");
}
This has no intrusive effects on the container specification.
I would like to point out that the last example is a typical mis-usage,
because the life-time of the C temporary has ended after the
construction of r3, but I added it anyway just to demonstrate that the
expected return type corresponds to the obtained one.
Now you can easily implement your suggested function like so:
template<class V>
auto unique(V&& v) -> set<typename
remove_reference<V>::type::value_type>
{
auto r = make_iterator_range(forward<V>(v));
return set<typename remove_reference<V>::type::value_type>
(r.first, r.second);
}
The same way it would have been better if std::begin and std::end
templates were using perfect forwarding, instead of const and non-
const lvalue versions.
There is no fundamental reason why the standard could not be extended in
the future to realize that. You could write-up a proposal for this, for
example.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]