Note that there are some explanatory texts on larger screens.

plurals
  1. POswapping container with self referencing element
    primarykey
    data
    text
    <p>When a container contains elements which has a pointer to the container itself, the standard version of swap through pointer exchange doesn't work, and the swap doesn't check any such possibility. Is there any way to tell the container that standard swap doesn't work, and to use some different version? What is the best way to implement such swap?</p> <p>The following example explains the problem.</p> <pre><code>template&lt;template&lt;typename T,typename = std::allocator&lt;T&gt; &gt; class H&gt; struct kid { typedef H&lt;kid&gt; home_type; typedef int id_t; kid(const home_type&amp; home,int m,typename home_type::size_type c=-1) : home_(&amp;home) , me_(m) , idx_(c) { } id_t me()const { return me_; } id_t cousin()const { return (*home_).size() &lt; idx_ ? -1 : (*home_)[idx_].me(); } private://default copy &amp; assign ok??? const home_type* home_; typename home_type::size_type idx_; id_t me_; }; int main() { typedef kid&lt;std::vector&gt; kid_t; typedef std::vector&lt;kid_t&gt; home_t; home_t home1,home2;//two neighbors home1.push_back(kid_t(home1,1));//elderly kid home1.push_back(kid_t(home1,2,0));//the cousins home1.push_back(kid_t(home1,3,0)); home1.push_back(kid_t(home1,4,0)); home2.push_back(kid_t(home2,10));//first kid home2.push_back(kid_t(home2,20));//second kid home2.push_back(kid_t(home2,30,0));//cousin of first kid. home2.push_back(kid_t(home2,40,1));//cousin of second kid for(home_t::const_iterator it = home1.begin(); it!= home1.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } std::cout&lt;&lt;"\n"; for(home_t::const_iterator it = home2.begin(); it!= home2.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } std::cout&lt;&lt;"\nexchange home\n"; std::swap(home1,home2);//they exchanged their house, surely this doesn't work. //what is the best way to do this? for(home_t::const_iterator it = home1.begin(); it!= home1.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } std::cout&lt;&lt;"\n"; for(home_t::const_iterator it = home2.begin(); it!= home2.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } //all the kids got confused, they points to neighborhood kids as their cousins } </code></pre> <p>In actual implementation, i have a class X with two vectors v1 &amp; v2, while elements of v1 points to portion of v2. Simply swapping v1 &amp; v2 doesn't work, as v1 contains pointer to v2 which also need to be updated. I am looking for the best way to implement such swap, without violating any encapsulation. </p> <p>UPDATE</p> <p>while i strongly dislike a public set_home for kid, as that is not part of kid's api and violates encapsulation, i also realized std::vector is not good. A dedicated home class is needed. A pointer based solution can always be done, as every level of indirection gives additional maneuverability. But that merely changes the problem rather than solving it.</p> <p>One thing should be noted is that kids are not freestanding objects, they need home, and that is encoded in signature of kid. I feel that, a nothrow swap just swaps the pointer is too optimistic view. The swap for container should know whether it can do that optimization. This kind of problem also happens when storing std::vector of std::tr1::function, where the functor itself stores the reference/iterator to the vector.</p> <p>I have this solution which just gives selective access to <em>set_home</em> to the home class using friendship, and stored as value rather than pointer.</p> <p>But i still don't like the solution very much as i feel that swap should be intelligent enough to check if it can do what it want to do, as it already has information about <em>value_type</em> (here kid instance) which it doesn't use at all.</p> <p>Here is the updated code.</p> <pre><code>template&lt;template&lt;typename T,typename = std::allocator&lt;T&gt; &gt; class H&gt; struct kid { typedef H&lt;kid&gt; home_type; typedef int id_t; kid(const home_type&amp; home,int m,typename home_type::size_type c=-1) : home_(&amp;home) , me_(m) , idx_(c) { } id_t me()const { return me_; } id_t cousin()const { return (*home_).size() &lt; idx_ ? -1 : (*home_)[idx_].me(); } private: friend home_type; void relocate(home_type&amp; home) {//private, not part of api.only home_type can call it. home_ = &amp;home; } private://default copy &amp; assign ok??? const home_type* home_; typename home_type::size_type idx_; id_t me_; }; template&lt;typename T,typename Alloc = std::allocator&lt;T&gt; &gt; class home { typedef std::vector&lt;T&gt; base_t; base_t h_; public: typedef typename base_t::size_type size_type; typedef typename base_t::iterator iterator; typedef typename base_t::const_iterator const_iterator; void push_back(const T&amp; t) { h_.push_back(t); } size_type size()const { return h_.size(); } const T&amp; operator[](size_type i)const { return h_[i]; } iterator begin() { return h_.begin(); } iterator end() { return h_.end(); } void swap(home&lt;T,Alloc&gt;&amp; other)//nothrow swap if allocators are equal, though O(N). { using std::swap; swap(h_,other.h_); for(iterator i = begin(), e = end();i!+ e; ++i) { (*i).relocate(h_); } for(iterator i = other.begin(), e = other.end();i!+ e; ++i) { (*i).relocate(other.h_); } } }; int main() { typedef kid&lt;home&gt; kid_t; typedef home&lt;kid_t&gt; home_t; home_t home1,home2;//two neighbors home1.push_back(kid_t(home1,1));//elderly kid home1.push_back(kid_t(home1,2,0));//the cousins home1.push_back(kid_t(home1,3,0)); home1.push_back(kid_t(home1,4,0)); home2.push_back(kid_t(home2,10));//first kid home2.push_back(kid_t(home2,20));//second kid home2.push_back(kid_t(home2,30,0));//cousin of first kid. home2.push_back(kid_t(home2,40,1));//cousin of second kid for(home_t::const_iterator it = home1.begin(); it!= home1.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } std::cout&lt;&lt;"\n"; for(home_t::const_iterator it = home2.begin(); it!= home2.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } std::cout&lt;&lt;"\nexchange home\n"; using std::swap; swap(home1,home2);//they exchanged their house, surely this doesn't work. //what is the best way to do this? for(home_t::const_iterator it = home1.begin(); it!= home1.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } std::cout&lt;&lt;"\n"; for(home_t::const_iterator it = home2.begin(); it!= home2.end(); ++it) { std::cout&lt;&lt;(*it).me()&lt;&lt;" "&lt;&lt;(*it).cousin()&lt;&lt;"\n"; } //everything is fine here. } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload