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 use std::make_move_iterator to do so), but in generic code I think that using std::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.










share|improve this question




















  • 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 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










  • 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















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 use std::make_move_iterator to do so), but in generic code I think that using std::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.










share|improve this question




















  • 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 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










  • 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













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 use std::make_move_iterator to do so), but in generic code I think that using std::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.










share|improve this question















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 use std::make_move_iterator to do so), but in generic code I think that using std::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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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 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










  • 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




    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 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










  • 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

















active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















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






























active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes
















 

draft saved


draft discarded



















































 


draft saved


draft discarded














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





















































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







這個網誌中的熱門文章

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud

Zucchini