Is this visitor implementation correct?

I am implementing a visitor in order to use it with the boost variant library. I want to know if is correct to specialize boost::static_visitor<> with a const reference type.

Note my question here is the following:

There are any problem specializing boost::static_visitor<> to boost::static_visitor<const T&> ?

template<typename T>
struct my_visitor : public boost::static_visitor<const T&> {

    template<typename U> const T& operator()(U& u) const {
        // some code here .....
        return X<U>::get_some_t(); // finally return some T.
    }
};

Upvotes: 3

Views: 380

Answers (2)

Jan Tibar
Jan Tibar

Reputation: 46

What about this, is this neat?

public interface Worker {
  <T> T accept( Visitor<T> visitor);
}

public class Developer implements Worker { 
  @Override
  public <T> T accept( Visitor<T> visitor ) {
    return visitor.workAsDeveloper( this );
  }
}

public class Manager implements Worker {
  @Override
  public <T> T accept( Visitor<T> visitor ) {
    return visitor.manageAsManager( this );
  }
}

public class Boss implements Worker {  
  @Override
  public <T> T accept( Visitor<T> visitor ) {
    return visitor.planLikeBoss( this );
  }
}

public interface Visitor<T> {
  T workAsDeveloper( Developer type );
  T manageAsManager( Manager type );
  T planLikeBoss( Boss type );
}

public class VisitorImpl implements Visitor<String> {
  
  @Override
  public String workAsDeveloper( Developer type ) {
    return "Working hard as a Developer";
  }
  
  @Override
  public String manageAsManager( Manager type ) {
    return "Managing smoothly as a Manager";
  }
  
  @Override
  public String planLikeBoss( Boss type ) {
    return "Planning like a Boss";
  }
}

public class App {
  Visitor<String> visitor;
  
  public App( Visitor<String> visitor ) {
    this.visitor = visitor;
  }
  
  public static void main( String[] args ) {
    // Given
    App app = new App( new VisitorImpl() );
    
    Developer developer = new Developer();
    Manager manager = new Manager();
    Boss boss = new Boss();
    
    // When
    app.run( developer );
    app.run( manager );
    app.run( boss );
  }
  
  void run( Worker worker ) {
    System.out.println( worker.accept( visitor ) );
  }
}

Upvotes: 0

sehe
sehe

Reputation: 393174

There is no problem as long as you don't return a reference to a local/temporary.

Also, be sure to check the validity of the reference over time (it ends when the variant object is destructed, which is when the variant itself is destructed, or (!) when it is reinitialized).

Background and explanation

A variant contains an object of the "current" element type, and you can reference into that object perfectly fine. As long as the variant is not reinitialized to another element type (in which case the reference is "just" dangling, exactly like it would if the lifetime of the referred-to object had ended).

So if get_somet_t() returns a T& or T const& (or something with a suitable implicit conversion) there is no problem.

In a simpler setting let me demonstrate valid options:

variant<int, std::string> v1 = 42;

int& i1 = get<int>(v1); // returns by ref, valid
i1 *= 2;

// now v1 contains the updated integer value 84

Likewise, you can even make variants of /just references/:

std::string s = "hello";
int answer = 42;

variant<int&, std::string&> v2(s);
get<std::string&>(v2) += " world"; // now s contains "hello world"

variant<int&, std::string&> v3(answer);
get<int&>(v3) *= 2; // now `answer` contains 84

See it all Live On Coliru


Demonstration

Put yet another way, the following is fine:

struct A { std::string a_property; };
struct B { std::string b_field;    };

struct select_member : static_visitor<std::string&> {
    std::string& operator()(A& a) const { return a.a_property; }
    std::string& operator()(B& b) const { return b.b_field;    }
};

int main()
{
    variant<A,B> v = A { "some string" };

    apply_visitor(select_member(), v) += " suffix";

    std::cout << get<A>(v).a_property << "\n"; // prints "some string suffix"
}

See it Live On Coliru as well.

Upvotes: 2

Related Questions