c++void-pointerspure-virtual

What is the correct way to write pure virtual function without a specific return type?


I'm trying to write an abstract struct named Shape, and I need a function in derived classes that will be counting distance from a ray origin to my shape. I use functions from here, they return float, vec2, vec3, etc. I do this because I need to store these shapes in a vector of BasePrimitive later. Is this a good way?

struct Shape {
    virtual void* Intersect(const vec3& ro, const vec3& rd) = 0; // ro - rayOrigin, rd - rayDirection
};

struct Sphere : public Shape {
    vec3 ce; // center
    float ra; // radius

    vec2 Intersect(const vec3& ro, const vec3& rd) override {
        // some code
    } 
};

I tried to do it via void*, but when I inserted void* I got an error:

return type is not identical to nor covariant with return type "void *" of overridden virtual function "Shape::Intersect"


Solution

  • If you want a function that can return ANY TYPE, one option is to do something like this:

    #include <any> // For 'std::any' and 'std::any_cast' (Since C++17)
    #include <string>
    
    
    class BC // Abstract polymorphic base class
    {
    public:
    
        virtual std::any f() = 0; // Pure virtual
    
        virtual ~BC() = default; // Virtual destructor (For an abstract polymorphic base class, it is highly recommended to provide a virtual destructor)
    };
    
    
    class DC1 final : public BC
    {
    public:
    
        std::any f() override
        {
            return 1.5; // double
        }
    };
    
    
    class DC2 final : public BC
    {
    public:
    
        std::any f() override
        {
            return std::string("Pluto"); // std::string
        }
    };
    
    
    class DC3 final : public BC
    {
    public:
    
        std::any f() override
        {
            return 0; // int
        }
    };
    
    
    int main()
    {
        DC1 obj1{};
        const auto a = std::any_cast<double>(obj1.f());
    
        DC2 obj2{};
        const auto b = std::any_cast<std::string>(obj2.f());
    
        DC3 obj3{};
        const auto c = std::any_cast<int>(obj3.f());
    }
    

    --

    For a SET OF TYPES this is a better choice:

    #include <variant> // For 'std::variant' and 'std::get' (Since C++17)
    #include <string>
    
    
    class BC // Abstract polymorphic base class
    {
    public:
        using SetOfTypes = std::variant<double, std::string, int>;
    
        virtual SetOfTypes f() = 0; // Pure virtual
    
        virtual ~BC() = default; // Virtual destructor (For an abstract polymorphic base class, it is highly recommended to provide a virtual destructor)
    };
    
    
    class DC1 final : public BC
    {
    public:
    
        SetOfTypes f() override
        {
            return 1.5; // double
        }
    };
    
    
    class DC2 final : public BC
    {
    public:
    
        SetOfTypes f() override
        {
            return std::string("Pluto"); // std::string
        }
    };
    
    
    class DC3 final : public BC
    {
    public:
    
        SetOfTypes f() override
        {
            return 0; // int
        }
    };
    
    
    int main()
    {
        DC1 obj1{};
        const auto a = std::get<double>(obj1.f());
    
        DC2 obj2{};
        const auto b = std::get<std::string>(obj2.f());
    
        DC3 obj3{};
        const auto c = std::get<int>(obj3.f());
    }