admin管理员组

文章数量:1192124

Lately I have been an avid user of implementation visitor pattern into my code. I was used to the classic way of implementation but since we have the std::variant functionality from c++ 17. I tend to use std::visit together with variant inorder to implement visitor pattern. However there are limitations that I have found using the std::visit functionality.

My understanding of std::visit is that it takes in a lambda or a struct/class implementing each individual item from the list of variant types. However my observation is that when I implement a class with hierarchy and the parent class implements the operator() overloads and children object calls it. It works!. However if I implement additional operator() overloads in the child class which has no relation to variant types. The code breaks and throws an error telling std::visit requires the visitor to be exhaustive.

`

#include <iostream>
#include <variant>
#include <string>
#include <vector>

using TYPE = std::variant<int, double, float, std::string, std::monostate>;
class parentVisitor {
public:
    parentVisitor() = default;
    std::string operator()(const int value) const {return std::to_string(value);}
    std::string operator()(const double value) const {return std::to_string(value);}
    std::string operator()(const float value) const {return std::to_string(value);}
    std::string operator()(std::string value) const {return value;}
    std::string operator()(std::monostate) const {return "";}
};

class childVisitor : public parentVisitor {
private:
    TYPE m_value;
public:

    // The below line creates the error!
    void operator()(std::vector<int> value) const {}


    explicit childVisitor(TYPE value) : m_value(std::move(value)) {};

    void printValue() {
        const std::string text = std::visit(*this, m_value);
        std::cout << text << std::endl;
    }
};

int main() {
    std::string text = "CPP";
    childVisitor visitor(text);
    visitor.printValue();
    return 0;
}

Error Message is shown below:

error: static assertion failed due to requirement 'is_invocable_v<childVisitor &, int &>': `std::visit` requires the visitor to be exhaustive.
  611 |     static_assert(is_invocable_v<_Visitor, _Values...>, "`std::visit` requires the visitor to be exhaustive.");

I think the intention of std::visit is use lambda function or a structure/class with a hierarchy implementing unique operator() overloads.

But let's try abusing the std::visit by implementing a function in parent class which could be called from the child class which will enforce the visitor pattern.

`

#include <iostream>
#include <variant>
#include <string>
#include <vector>

using TYPE = std::variant<int, double, float, std::string, std::monostate>;
class parentVisitor {
public:
    parentVisitor() = default;
    std::string operator()(const int value) const {return std::to_string(value);}
    std::string operator()(const double value) const {return std::to_string(value);}
    std::string operator()(const float value) const {return std::to_string(value);}
    std::string operator()(std::string value) const {return value;}
    std::string operator()(std::monostate) const {return "";}


    std::string visit_parent(TYPE type) {
        return std::visit(*this, type);
    }

};

class childVisitor : public parentVisitor {
private:
    TYPE m_value;
public:

    // The below line creates the error!
    void operator()(std::vector<int> value) const {}


    explicit childVisitor(TYPE value) : m_value(std::move(value)) {};

    void printValue() {
        //const std::string text = std::visit(*this, m_value);
        std::string text = visit_parent(m_value);
        std::cout << text << std::endl;
    }
};

int main() {
    std::string text = "CPP";
    childVisitor visitor(text);
    visitor.printValue();
    return 0;
}

Now this works because the parent class has clear implementation of operator() overloads.

Let me know it makes sense and is there any other way of implementing visitor functionality from a hierarchy.

Lately I have been an avid user of implementation visitor pattern into my code. I was used to the classic way of implementation but since we have the std::variant functionality from c++ 17. I tend to use std::visit together with variant inorder to implement visitor pattern. However there are limitations that I have found using the std::visit functionality.

My understanding of std::visit is that it takes in a lambda or a struct/class implementing each individual item from the list of variant types. However my observation is that when I implement a class with hierarchy and the parent class implements the operator() overloads and children object calls it. It works!. However if I implement additional operator() overloads in the child class which has no relation to variant types. The code breaks and throws an error telling std::visit requires the visitor to be exhaustive.

`

#include <iostream>
#include <variant>
#include <string>
#include <vector>

using TYPE = std::variant<int, double, float, std::string, std::monostate>;
class parentVisitor {
public:
    parentVisitor() = default;
    std::string operator()(const int value) const {return std::to_string(value);}
    std::string operator()(const double value) const {return std::to_string(value);}
    std::string operator()(const float value) const {return std::to_string(value);}
    std::string operator()(std::string value) const {return value;}
    std::string operator()(std::monostate) const {return "";}
};

class childVisitor : public parentVisitor {
private:
    TYPE m_value;
public:

    // The below line creates the error!
    void operator()(std::vector<int> value) const {}


    explicit childVisitor(TYPE value) : m_value(std::move(value)) {};

    void printValue() {
        const std::string text = std::visit(*this, m_value);
        std::cout << text << std::endl;
    }
};

int main() {
    std::string text = "CPP";
    childVisitor visitor(text);
    visitor.printValue();
    return 0;
}

Error Message is shown below:

error: static assertion failed due to requirement 'is_invocable_v<childVisitor &, int &>': `std::visit` requires the visitor to be exhaustive.
  611 |     static_assert(is_invocable_v<_Visitor, _Values...>, "`std::visit` requires the visitor to be exhaustive.");

I think the intention of std::visit is use lambda function or a structure/class with a hierarchy implementing unique operator() overloads.

But let's try abusing the std::visit by implementing a function in parent class which could be called from the child class which will enforce the visitor pattern.

`

#include <iostream>
#include <variant>
#include <string>
#include <vector>

using TYPE = std::variant<int, double, float, std::string, std::monostate>;
class parentVisitor {
public:
    parentVisitor() = default;
    std::string operator()(const int value) const {return std::to_string(value);}
    std::string operator()(const double value) const {return std::to_string(value);}
    std::string operator()(const float value) const {return std::to_string(value);}
    std::string operator()(std::string value) const {return value;}
    std::string operator()(std::monostate) const {return "";}


    std::string visit_parent(TYPE type) {
        return std::visit(*this, type);
    }

};

class childVisitor : public parentVisitor {
private:
    TYPE m_value;
public:

    // The below line creates the error!
    void operator()(std::vector<int> value) const {}


    explicit childVisitor(TYPE value) : m_value(std::move(value)) {};

    void printValue() {
        //const std::string text = std::visit(*this, m_value);
        std::string text = visit_parent(m_value);
        std::cout << text << std::endl;
    }
};

int main() {
    std::string text = "CPP";
    childVisitor visitor(text);
    visitor.printValue();
    return 0;
}

Now this works because the parent class has clear implementation of operator() overloads.

Let me know it makes sense and is there any other way of implementing visitor functionality from a hierarchy.

Share Improve this question asked Jan 24 at 2:36 Sukrit GhoraiSukrit Ghorai 111 bronze badge 1
  • 3 You need to add using parentVisitor::operator(); to your childVisitor for the first example. – Stephen Newell Commented Jan 24 at 2:46
Add a comment  | 

1 Answer 1

Reset to default 3

This is a normal feature of inheritance in C++. Defining a method in a derived class hides the method of the same name from the parent class. For instance, the following will fail to compile for the same reason as your first program:

struct Parent
{
    void foo(int) {}
};

struct Child : Parent
{
    void foo(std::string) {}
};

int main()
{
    Child c;
    c.foo(42);
}

Demo

You can bring the parent class's function into the child class's scope with the using keyword:

struct Parent
{
    void foo(int) {}
};

struct Child : Parent
{
    using Parent::foo;  // <--- Add this
    void foo(std::string) {}
};

int main()
{
    Child c;
    c.foo(42);
}

Demo


You can do the same thing in your childVisitor by adding using parentVisitor::operator()

本文标签: cUsing and abusing stdvisitStack Overflow