Perfect forwarding iterators: is it too much?
up vote
0
down vote
favorite
In C++, the constness of an object is forwarded through iterators. std::begin(a)
will generally return A::iterator
if a
is not const, A::const_iterator
if a
is const.
I tried to extend this concept to move behaviour, in order to have better generic code. The basic idea is something like this:
struct A {
using value_type = std::string;
using iterator = AIter<A>;
using const_iterator = AIter<A const>;
using move_iterator = AIter<A &&>;
iterator begin() &;
iterator end() &;
const_iterator begin() const &;
const_iterator end() const &;
move_iterator begin() &&;
move_iterator end() &&;
};
template <typename A> struct AIter {
using a_type = A;
using value_type = typename std::decay_t<A>::value_type;
using reference = std::conditional_t<
std::is_rvalue_reference_v<A>, value_type &&,
std::conditional_t<std::is_const_v<std::remove_reference_t<A>>,
value_type const &, value_type &>>;
};
It is a bit convoluted, especially the conditional type in the iterator. The concept is simply that the type of the iterator completely depends on the reference type of A
.
Unfortunately, the problem arises when, instead of directly using member functions, I try to use std::begin
and std::end
(and all the similar ones), because they are not designed to work in a perfect forwarding manner. Here you can find a small demo on compiler explorer, if you want to make some tests.
Given the current situation, my point is:
- Am I asking too much to the standard iterators? Are there any unexpected behaviour of this approach? I have never seen something like
std::begin(std::move(a))
around (you generally usestd::make_move_iterator
to do so), but in generic code I think that usingstd::begin(std::forward<A>(a))
could be useful... or not? - I don't have a clear idea of how this approach would work with C++20 ranges. What do you think?
- If you consider that this approach could be valuable, do you think that it could be better using the
.begin()
method or a custom free function that performs perfect forwarding? Something like in the link to compiler explorer.
EDIT: Nelfeal found this other question that is very related to mine.
c++ iterator c++17 perfect-forwarding
|
show 10 more comments
up vote
0
down vote
favorite
In C++, the constness of an object is forwarded through iterators. std::begin(a)
will generally return A::iterator
if a
is not const, A::const_iterator
if a
is const.
I tried to extend this concept to move behaviour, in order to have better generic code. The basic idea is something like this:
struct A {
using value_type = std::string;
using iterator = AIter<A>;
using const_iterator = AIter<A const>;
using move_iterator = AIter<A &&>;
iterator begin() &;
iterator end() &;
const_iterator begin() const &;
const_iterator end() const &;
move_iterator begin() &&;
move_iterator end() &&;
};
template <typename A> struct AIter {
using a_type = A;
using value_type = typename std::decay_t<A>::value_type;
using reference = std::conditional_t<
std::is_rvalue_reference_v<A>, value_type &&,
std::conditional_t<std::is_const_v<std::remove_reference_t<A>>,
value_type const &, value_type &>>;
};
It is a bit convoluted, especially the conditional type in the iterator. The concept is simply that the type of the iterator completely depends on the reference type of A
.
Unfortunately, the problem arises when, instead of directly using member functions, I try to use std::begin
and std::end
(and all the similar ones), because they are not designed to work in a perfect forwarding manner. Here you can find a small demo on compiler explorer, if you want to make some tests.
Given the current situation, my point is:
- Am I asking too much to the standard iterators? Are there any unexpected behaviour of this approach? I have never seen something like
std::begin(std::move(a))
around (you generally usestd::make_move_iterator
to do so), but in generic code I think that usingstd::begin(std::forward<A>(a))
could be useful... or not? - I don't have a clear idea of how this approach would work with C++20 ranges. What do you think?
- If you consider that this approach could be valuable, do you think that it could be better using the
.begin()
method or a custom free function that performs perfect forwarding? Something like in the link to compiler explorer.
EDIT: Nelfeal found this other question that is very related to mine.
c++ iterator c++17 perfect-forwarding
1
Const versions of iterators are important for const-correctness. I'm pretty sure move versions wouldn't have any advantage overstd::make_move_iterator
, even in generic contexts. If you see a good use case one, you probably should include it in your question.
– Nelfeal
Nov 7 at 11:37
For "generic context", I intend code like this (a dumb one in this case). As you can see, using the standard behaviour I must use compile-time conditionals in order to perform the operation one would expect.
– dodomorandi
Nov 7 at 11:57
"a dumb one in this case" becausefrom_to
doesn't do anything more than whatstd::transform
does? These kinds of algorithms are defined, at least in the standard library, for iterators instead of containers. I can't think of a example that isn't a "dumb one".
– Nelfeal
Nov 7 at 12:11
I have a real test case in which I am using this behaviour. It is convoluted and it does not make any sense to create complex example just to demonstrate that "there is an application". Asserting that "there is no possible useful usage" is a bit strong, and until you can give a valid proof it is just your opinion IMHO.
– dodomorandi
Nov 7 at 12:19
Of course it's my opinion, I've never said otherwise. But without a "real" test case, even a convoluted one, I fail to see a useful application, and I doubt anyone else who stumbles upon your question would see one.
– Nelfeal
Nov 7 at 12:39
|
show 10 more comments
up vote
0
down vote
favorite
up vote
0
down vote
favorite
In C++, the constness of an object is forwarded through iterators. std::begin(a)
will generally return A::iterator
if a
is not const, A::const_iterator
if a
is const.
I tried to extend this concept to move behaviour, in order to have better generic code. The basic idea is something like this:
struct A {
using value_type = std::string;
using iterator = AIter<A>;
using const_iterator = AIter<A const>;
using move_iterator = AIter<A &&>;
iterator begin() &;
iterator end() &;
const_iterator begin() const &;
const_iterator end() const &;
move_iterator begin() &&;
move_iterator end() &&;
};
template <typename A> struct AIter {
using a_type = A;
using value_type = typename std::decay_t<A>::value_type;
using reference = std::conditional_t<
std::is_rvalue_reference_v<A>, value_type &&,
std::conditional_t<std::is_const_v<std::remove_reference_t<A>>,
value_type const &, value_type &>>;
};
It is a bit convoluted, especially the conditional type in the iterator. The concept is simply that the type of the iterator completely depends on the reference type of A
.
Unfortunately, the problem arises when, instead of directly using member functions, I try to use std::begin
and std::end
(and all the similar ones), because they are not designed to work in a perfect forwarding manner. Here you can find a small demo on compiler explorer, if you want to make some tests.
Given the current situation, my point is:
- Am I asking too much to the standard iterators? Are there any unexpected behaviour of this approach? I have never seen something like
std::begin(std::move(a))
around (you generally usestd::make_move_iterator
to do so), but in generic code I think that usingstd::begin(std::forward<A>(a))
could be useful... or not? - I don't have a clear idea of how this approach would work with C++20 ranges. What do you think?
- If you consider that this approach could be valuable, do you think that it could be better using the
.begin()
method or a custom free function that performs perfect forwarding? Something like in the link to compiler explorer.
EDIT: Nelfeal found this other question that is very related to mine.
c++ iterator c++17 perfect-forwarding
In C++, the constness of an object is forwarded through iterators. std::begin(a)
will generally return A::iterator
if a
is not const, A::const_iterator
if a
is const.
I tried to extend this concept to move behaviour, in order to have better generic code. The basic idea is something like this:
struct A {
using value_type = std::string;
using iterator = AIter<A>;
using const_iterator = AIter<A const>;
using move_iterator = AIter<A &&>;
iterator begin() &;
iterator end() &;
const_iterator begin() const &;
const_iterator end() const &;
move_iterator begin() &&;
move_iterator end() &&;
};
template <typename A> struct AIter {
using a_type = A;
using value_type = typename std::decay_t<A>::value_type;
using reference = std::conditional_t<
std::is_rvalue_reference_v<A>, value_type &&,
std::conditional_t<std::is_const_v<std::remove_reference_t<A>>,
value_type const &, value_type &>>;
};
It is a bit convoluted, especially the conditional type in the iterator. The concept is simply that the type of the iterator completely depends on the reference type of A
.
Unfortunately, the problem arises when, instead of directly using member functions, I try to use std::begin
and std::end
(and all the similar ones), because they are not designed to work in a perfect forwarding manner. Here you can find a small demo on compiler explorer, if you want to make some tests.
Given the current situation, my point is:
- Am I asking too much to the standard iterators? Are there any unexpected behaviour of this approach? I have never seen something like
std::begin(std::move(a))
around (you generally usestd::make_move_iterator
to do so), but in generic code I think that usingstd::begin(std::forward<A>(a))
could be useful... or not? - I don't have a clear idea of how this approach would work with C++20 ranges. What do you think?
- If you consider that this approach could be valuable, do you think that it could be better using the
.begin()
method or a custom free function that performs perfect forwarding? Something like in the link to compiler explorer.
EDIT: Nelfeal found this other question that is very related to mine.
c++ iterator c++17 perfect-forwarding
c++ iterator c++17 perfect-forwarding
edited Nov 7 at 16:24
Lightness Races in Orbit
278k51445762
278k51445762
asked Nov 7 at 11:17
dodomorandi
503514
503514
1
Const versions of iterators are important for const-correctness. I'm pretty sure move versions wouldn't have any advantage overstd::make_move_iterator
, even in generic contexts. If you see a good use case one, you probably should include it in your question.
– Nelfeal
Nov 7 at 11:37
For "generic context", I intend code like this (a dumb one in this case). As you can see, using the standard behaviour I must use compile-time conditionals in order to perform the operation one would expect.
– dodomorandi
Nov 7 at 11:57
"a dumb one in this case" becausefrom_to
doesn't do anything more than whatstd::transform
does? These kinds of algorithms are defined, at least in the standard library, for iterators instead of containers. I can't think of a example that isn't a "dumb one".
– Nelfeal
Nov 7 at 12:11
I have a real test case in which I am using this behaviour. It is convoluted and it does not make any sense to create complex example just to demonstrate that "there is an application". Asserting that "there is no possible useful usage" is a bit strong, and until you can give a valid proof it is just your opinion IMHO.
– dodomorandi
Nov 7 at 12:19
Of course it's my opinion, I've never said otherwise. But without a "real" test case, even a convoluted one, I fail to see a useful application, and I doubt anyone else who stumbles upon your question would see one.
– Nelfeal
Nov 7 at 12:39
|
show 10 more comments
1
Const versions of iterators are important for const-correctness. I'm pretty sure move versions wouldn't have any advantage overstd::make_move_iterator
, even in generic contexts. If you see a good use case one, you probably should include it in your question.
– Nelfeal
Nov 7 at 11:37
For "generic context", I intend code like this (a dumb one in this case). As you can see, using the standard behaviour I must use compile-time conditionals in order to perform the operation one would expect.
– dodomorandi
Nov 7 at 11:57
"a dumb one in this case" becausefrom_to
doesn't do anything more than whatstd::transform
does? These kinds of algorithms are defined, at least in the standard library, for iterators instead of containers. I can't think of a example that isn't a "dumb one".
– Nelfeal
Nov 7 at 12:11
I have a real test case in which I am using this behaviour. It is convoluted and it does not make any sense to create complex example just to demonstrate that "there is an application". Asserting that "there is no possible useful usage" is a bit strong, and until you can give a valid proof it is just your opinion IMHO.
– dodomorandi
Nov 7 at 12:19
Of course it's my opinion, I've never said otherwise. But without a "real" test case, even a convoluted one, I fail to see a useful application, and I doubt anyone else who stumbles upon your question would see one.
– Nelfeal
Nov 7 at 12:39
1
1
Const versions of iterators are important for const-correctness. I'm pretty sure move versions wouldn't have any advantage over
std::make_move_iterator
, even in generic contexts. If you see a good use case one, you probably should include it in your question.– Nelfeal
Nov 7 at 11:37
Const versions of iterators are important for const-correctness. I'm pretty sure move versions wouldn't have any advantage over
std::make_move_iterator
, even in generic contexts. If you see a good use case one, you probably should include it in your question.– Nelfeal
Nov 7 at 11:37
For "generic context", I intend code like this (a dumb one in this case). As you can see, using the standard behaviour I must use compile-time conditionals in order to perform the operation one would expect.
– dodomorandi
Nov 7 at 11:57
For "generic context", I intend code like this (a dumb one in this case). As you can see, using the standard behaviour I must use compile-time conditionals in order to perform the operation one would expect.
– dodomorandi
Nov 7 at 11:57
"a dumb one in this case" because
from_to
doesn't do anything more than what std::transform
does? These kinds of algorithms are defined, at least in the standard library, for iterators instead of containers. I can't think of a example that isn't a "dumb one".– Nelfeal
Nov 7 at 12:11
"a dumb one in this case" because
from_to
doesn't do anything more than what std::transform
does? These kinds of algorithms are defined, at least in the standard library, for iterators instead of containers. I can't think of a example that isn't a "dumb one".– Nelfeal
Nov 7 at 12:11
I have a real test case in which I am using this behaviour. It is convoluted and it does not make any sense to create complex example just to demonstrate that "there is an application". Asserting that "there is no possible useful usage" is a bit strong, and until you can give a valid proof it is just your opinion IMHO.
– dodomorandi
Nov 7 at 12:19
I have a real test case in which I am using this behaviour. It is convoluted and it does not make any sense to create complex example just to demonstrate that "there is an application". Asserting that "there is no possible useful usage" is a bit strong, and until you can give a valid proof it is just your opinion IMHO.
– dodomorandi
Nov 7 at 12:19
Of course it's my opinion, I've never said otherwise. But without a "real" test case, even a convoluted one, I fail to see a useful application, and I doubt anyone else who stumbles upon your question would see one.
– Nelfeal
Nov 7 at 12:39
Of course it's my opinion, I've never said otherwise. But without a "real" test case, even a convoluted one, I fail to see a useful application, and I doubt anyone else who stumbles upon your question would see one.
– Nelfeal
Nov 7 at 12:39
|
show 10 more comments
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53188406%2fperfect-forwarding-iterators-is-it-too-much%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Const versions of iterators are important for const-correctness. I'm pretty sure move versions wouldn't have any advantage over
std::make_move_iterator
, even in generic contexts. If you see a good use case one, you probably should include it in your question.– Nelfeal
Nov 7 at 11:37
For "generic context", I intend code like this (a dumb one in this case). As you can see, using the standard behaviour I must use compile-time conditionals in order to perform the operation one would expect.
– dodomorandi
Nov 7 at 11:57
"a dumb one in this case" because
from_to
doesn't do anything more than whatstd::transform
does? These kinds of algorithms are defined, at least in the standard library, for iterators instead of containers. I can't think of a example that isn't a "dumb one".– Nelfeal
Nov 7 at 12:11
I have a real test case in which I am using this behaviour. It is convoluted and it does not make any sense to create complex example just to demonstrate that "there is an application". Asserting that "there is no possible useful usage" is a bit strong, and until you can give a valid proof it is just your opinion IMHO.
– dodomorandi
Nov 7 at 12:19
Of course it's my opinion, I've never said otherwise. But without a "real" test case, even a convoluted one, I fail to see a useful application, and I doubt anyone else who stumbles upon your question would see one.
– Nelfeal
Nov 7 at 12:39