简体   繁体   中英

Using visitor pattern without writing too many overloads

I have two datatypes called DragonVector and UnbiasedDragon and I am using visitor pattern for dynamic type inference.

I want to extend a DragonVector only by a DragonVector and similarly for UnbiasedDragon .

I have the following code for extending the vectors:

template<class T>
class ExtendVisitor{
    public:
    void operator()(DragonVector<T>& vec1, const DragonVector<T>& vec2){
        vec1.extend(vec2);
    }
    void operator()(UnbiasedDragon<T>& vec1, const UnbiasedDragon<T>& vec2){
        vec1.extend(vec2);
    }
    void operator()(auto& vec1, const auto& vec2){
        std::cout<<"wrong class"<<std::endl;
    } 
};

I get error: 'auto' not allowed in function prototype . I am using C++17.

Since, there are only two classes, I can exhaustively write the operator overloads in the visitor for all the combinations. But this seems infeasible as the number of classes grow large.

I tried using templating as a work around as

template<class T>
class ExtendVisitor{
    public:
    void operator()(DragonVector<T>& vec1, const DragonVector<T>& vec2){
        vec1.extend(vec2);
    }
    void operator()(UnbiasedDragon<T>& vec1, const UnbiasedDragon<T>& vec2){
        vec1.extend(vec2);
    }
    template<class TT>
    void operator()(TT& vec1, const TT& vec2){
        std::cout<<"wrong class"<<std::endl;
    } 
};

but this also did not work out.

Is there a way to use visitor pattern without having to write all the possible combinations?

You can make it a template to catch all other combinations. In your attempt your method has both arguments of same type, but you want different types:

template <typename A,typename B>
void operator()(A& vec1, const B& vec2){
    std::cout<<"wrong class"<<std::endl;
} 

Alternatively use lambdas as in the example here: https://en.cppreference.com/w/cpp/utility/variant/visit . Generic lambdas are available since C++14 already.

The syntax in the first snippet is available starting with C++20 for which it would work as expected.

The second snippet doesn't work only because you are forcing the two arguments to be the same case in the default overload. There should be two template parameters, one for each function parameter.

There is however no need to make it this complicated anyway. You can use a single template and then implement your condition (that the types are equal) in its body:

template<typename T, typename U>
void operator()(T& t, const U& u){
    static_assert(!std::is_const_v<T>, "ExtendVisitor requires the left-hand to be modifiable!");

    if constexpr(std::is_same_v<T, U>) {
        vec1.extend(vec2);
    } else {
        // You should probably throw an exception here instead!
        std::cout<<"wrong class"<<std::endl;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM