Search code examples
c++boostboost-variantstatic-visitor

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.
    }
};

Solution

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