2

Situation

I'm designing a class template logic that supports move-semantics. logic has a template parameter Visitor and a reference member that's type is Visitor&. That is a library code.

Users inherits the class template logic and pass a custom visitor such as my_visitor. The custom visitor may contains movable members. For example, my_visitor has a member v that's type is std::vector.

Problem

See test2(). When I move my_logic, my_visitor::v is moved as expected. However, logic<Visitor>::vis refers to the moved from object. Is there any good way to refer to the moved to object?

#include <iostream> #include <vector> // Library code template <typename Visitor> // Concept: Visitor should have visit() struct logic { logic(Visitor& v):vis(v) {} void execute() { vis.visit(); } // Other APIs Visitor& vis; // Other member variables... }; // User code struct my_visitor { my_visitor() { v.push_back(42); } void visit() { std::cout << "expected 1, actual " << v.size() << std::endl; } std::vector<int> v; }; // User inherits all logic's APIs struct my_logic : logic<my_visitor> { my_logic():logic<my_visitor>(mv) {} my_visitor mv; }; void test1() { std::cout << "test1" << std::endl; my_logic m; m.execute(); } void test2() { std::cout << "test2" << std::endl; my_logic m1; { my_logic m2(std::move(m1)); // logic::vis refers to moved from my_visitor... m2.execute(); } } int main() { test1(); test2(); } 
1
  • You have to implement your move constructor for my_logic (and probably delete move assignment). Commented Apr 23, 2016 at 12:40

3 Answers 3

1

The problem is that my_logic has both a member (mv) and a reference to that member (vis) and you have to ensure that the reference is always referring to the same member. With the defaulted move constructor, the new reference vis still refers to the old member, which then gets moved from. That's why you end up at 0:

 m1.mv <-----+ m2.mv ↑ | | | | | m1.vis +------ m2.vis 

One solution is, as Jarod proposes, to write your own copy/move constructors/assignment operators to ensure that m2.vis is pointing to m2.mv.

However, I would suggest simply avoiding the extra reference by just using CRTP and having your base logic class refer directly to the derived one:

template <class Derived> struct logic { Derived& self() { return static_cast<Derived&>(*this); } void execute() { self().visit(); } }; struct my_visitor : logic<my_visitor) { my_visitor() { v.push_back(42); } void visit() { std::cout << "expected 1, actual " << v.size() << std::endl; } std::vector<int> v; }; 

This way, there's only one way to refer to the data - so nothing can get out of line.

Alternatively, you could explicitly delete the copy/move constructors and assignment operators of logic. This would require you to explicitly write your own for all derived types, but would ensure that you did it correctly. For instance:

logic(logic&& ) = delete; my_logic(my_logic&& rhs) : logic(mv) // always refer to me! , mv(std::move(rhs.mv)) { } 
Sign up to request clarification or add additional context in comments.

2 Comments

thank you the answer. It works perfectly! I changed the concept of the logic's template parameter. VisitorHolder shoud have the function visit() that returns a visitor that has visit() member function. I updated my code as melpon.org/wandbox/permlink/SvnLl0sR4JrOwMMX . I also support move assignment without reference wrapper as melpon.org/wandbox/permlink/xPFwfOuVavxmhcpw .
I copy/pasted URL for move assignment version is wrong. The corrent one is melpon.org/wandbox/permlink/L3I5A9zWhj2QM7UN .
1

Use std::reference_wrapper instead of a native reference:

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

Specifically, std::reference_wrapper is a CopyConstructible and CopyAssignable wrapper around a reference to object or reference to function of type T. Instances of std::reference_wrapper are objects (they can be copied or stored in containers) but they are implicitly convertible to T&, so that they can be used as arguments with the functions that take the underlying type by reference.

6 Comments

Thank you for the comment. I replaced them. Here is the original code melpon.org/wandbox/permlink/jbp876DbGDsYg6Q1 and the replaced code melpon.org/wandbox/permlink/x1QbEaOgDHmeaxjz How to update the reference wrapper vis?
@TakatoshiKondo: Looks like a different question to me.
my problem is how the class template logic supports a move-semantics with updating vis refer to moved to object.
I suppose not but you will need it anyway otherwise the visitor may not be assigned to.
I think that I can implement move assignment operator with native reference. It's a little tricky. See melpon.org/wandbox/permlink/L3I5A9zWhj2QM7UN line16-20
|
0

You have to write your own move/copy constructor

struct my_logic : logic<my_visitor> { my_logic():logic<my_visitor>(mv) {} my_visitor mv; my_logic(const my_logic& rhs) : logic<my_visitor>(mv), mv(rhs.mv) {} my_logic(my_logic&& rhs) : logic<my_visitor>(mv), mv(std::move(rhs.mv)) {} }; 

Demo

And with reference_wrapper, you may also implement assignment in similar way.

2 Comments

It seems that the move constructor for my_logic creates a new logic<my_visitor>. The class template logic has other member variables (I wrote a comment). It is line 18 in your demo. I'd like to move all member variables. I just updated you demo code as coliru.stacked-crooked.com/a/aa6ce7e2506423eb . I added a constructor for logic logic(logic<Visitor>&& other, Visitor& v):vis(v), s(std::move(other.s)) {} (line 9) and calling code my_logic(my_logic&& rhs) : logic<my_visitor>(std::move(rhs), mv), mv(std::move(rhs.mv)) {} (line 49). Then it works as expected. Thank you.
I realized that I can't write move assignment operator using this way. Because it requires two parameters. It seems that I need to write a different member function for that...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.