How to parametrize an algorithm with a custom non-pure swap-function?
I want to apply an algorithm from <algorithms>
on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap
function with state: just pointer to the container with elements to be able to make swap
s synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap
function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p };
and overloading of the free function void swap(P & l, P & r);
with the body of the lambda from the code above. Also I surely should to make values
a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap
function to the algorithms from <algorithm>
in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
|
show 2 more comments
I want to apply an algorithm from <algorithms>
on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap
function with state: just pointer to the container with elements to be able to make swap
s synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap
function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p };
and overloading of the free function void swap(P & l, P & r);
with the body of the lambda from the code above. Also I surely should to make values
a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap
function to the algorithms from <algorithm>
in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
Rather thanranges
being a container ofstd::pair<I, I>
, why not ofstruct { I first; I second; L & list; }
?
– Caleth
Nov 23 '18 at 9:17
As an aside, all yourp.second
s are elements of L of the formi, i
. Do you intend that?
– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Calethstd::pair< int, int >
elements are just for example.first
is just a number of a range here and thesecond
is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.
– Orient
Nov 23 '18 at 9:40
Forstruct { I first; I second; L * list = {}; }
one also need to overload move constructor and move assignment operator to interoperate w/std::swap
properly.
– Orient
Nov 23 '18 at 10:28
|
show 2 more comments
I want to apply an algorithm from <algorithms>
on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap
function with state: just pointer to the container with elements to be able to make swap
s synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap
function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p };
and overloading of the free function void swap(P & l, P & r);
with the body of the lambda from the code above. Also I surely should to make values
a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap
function to the algorithms from <algorithm>
in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
I want to apply an algorithm from <algorithms>
on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap
function with state: just pointer to the container with elements to be able to make swap
s synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap
function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p };
and overloading of the free function void swap(P & l, P & r);
with the body of the lambda from the code above. Also I surely should to make values
a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap
function to the algorithms from <algorithm>
in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
c++ algorithm lambda stl overload-resolution
edited Nov 23 '18 at 12:24
Orient
asked Nov 23 '18 at 9:07
OrientOrient
6,26153295
6,26153295
Rather thanranges
being a container ofstd::pair<I, I>
, why not ofstruct { I first; I second; L & list; }
?
– Caleth
Nov 23 '18 at 9:17
As an aside, all yourp.second
s are elements of L of the formi, i
. Do you intend that?
– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Calethstd::pair< int, int >
elements are just for example.first
is just a number of a range here and thesecond
is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.
– Orient
Nov 23 '18 at 9:40
Forstruct { I first; I second; L * list = {}; }
one also need to overload move constructor and move assignment operator to interoperate w/std::swap
properly.
– Orient
Nov 23 '18 at 10:28
|
show 2 more comments
Rather thanranges
being a container ofstd::pair<I, I>
, why not ofstruct { I first; I second; L & list; }
?
– Caleth
Nov 23 '18 at 9:17
As an aside, all yourp.second
s are elements of L of the formi, i
. Do you intend that?
– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Calethstd::pair< int, int >
elements are just for example.first
is just a number of a range here and thesecond
is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.
– Orient
Nov 23 '18 at 9:40
Forstruct { I first; I second; L * list = {}; }
one also need to overload move constructor and move assignment operator to interoperate w/std::swap
properly.
– Orient
Nov 23 '18 at 10:28
Rather than
ranges
being a container of std::pair<I, I>
, why not of struct { I first; I second; L & list; }
?– Caleth
Nov 23 '18 at 9:17
Rather than
ranges
being a container of std::pair<I, I>
, why not of struct { I first; I second; L & list; }
?– Caleth
Nov 23 '18 at 9:17
As an aside, all your
p.second
s are elements of L of the form i, i
. Do you intend that?– Caleth
Nov 23 '18 at 9:25
As an aside, all your
p.second
s are elements of L of the form i, i
. Do you intend that?– Caleth
Nov 23 '18 at 9:25
1
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Caleth
std::pair< int, int >
elements are just for example. first
is just a number of a range here and the second
is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.– Orient
Nov 23 '18 at 9:40
@Caleth
std::pair< int, int >
elements are just for example. first
is just a number of a range here and the second
is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.– Orient
Nov 23 '18 at 9:40
For
struct { I first; I second; L * list = {}; }
one also need to overload move constructor and move assignment operator to interoperate w/ std::swap
properly.– Orient
Nov 23 '18 at 10:28
For
struct { I first; I second; L * list = {}; }
one also need to overload move constructor and move assignment operator to interoperate w/ std::swap
properly.– Orient
Nov 23 '18 at 10:28
|
show 2 more comments
2 Answers
2
active
oldest
votes
You can just include values
in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
I find a better way (if compare with ones based on swap
function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
add a comment |
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',
autoActivateHeartbeat: false,
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
});
}
});
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%2f53443555%2fhow-to-parametrize-an-algorithm-with-a-custom-non-pure-swap-function%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can just include values
in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
You can just include values
in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
You can just include values
in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You can just include values
in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
answered Nov 23 '18 at 10:38
CalethCaleth
17.3k22139
17.3k22139
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
I find a better way (if compare with ones based on swap
function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
add a comment |
I find a better way (if compare with ones based on swap
function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
add a comment |
I find a better way (if compare with ones based on swap
function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
I find a better way (if compare with ones based on swap
function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
edited Nov 23 '18 at 10:49
answered Nov 23 '18 at 10:43
OrientOrient
6,26153295
6,26153295
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
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%2f53443555%2fhow-to-parametrize-an-algorithm-with-a-custom-non-pure-swap-function%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
Rather than
ranges
being a container ofstd::pair<I, I>
, why not ofstruct { I first; I second; L & list; }
?– Caleth
Nov 23 '18 at 9:17
As an aside, all your
p.second
s are elements of L of the formi, i
. Do you intend that?– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Caleth
std::pair< int, int >
elements are just for example.first
is just a number of a range here and thesecond
is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.– Orient
Nov 23 '18 at 9:40
For
struct { I first; I second; L * list = {}; }
one also need to overload move constructor and move assignment operator to interoperate w/std::swap
properly.– Orient
Nov 23 '18 at 10:28