在没有特定返回类型的情况下编写纯虚函数的正确方法是什么?

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

提问人:Kapibarovich Kapibarov 提问时间:10/17/2023 最后编辑:Remy LebeauKapibarovich Kapibarov 更新时间:10/20/2023 访问量:197

问:

我正在尝试编写一个名为 的抽象结构,我需要派生类中的函数来计算从射线原点到我的形状的距离。我使用这里的函数,它们返回 、 、 等。我这样做是因为我需要将这些形状存储在稍后的向量中。这是个好方法吗?Shapefloatvec2vec3BasePrimitive

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

我试图通过 来做到这一点,但是当我插入时出现错误:void*void*

返回类型与被覆盖的虚函数“Shape::Intersect”的返回类型“void *”不相同或协变

C++ void-pointers pure-virtual

评论

3赞 HolyBlackCat 10/17/2023
但是,为什么需要不同的退货类型呢?距离始终是一个数字。
0赞 Kapibarovich Kapibarov 10/17/2023
我使用 iquilezles.org/articles/intersectors 中的函数。其中的某些函数具有可能有用的可选参数。我想将它们保存在 Shape 的派生类中。
2赞 Drew Dormann 10/17/2023
错误消息是正确的。您承诺返回类型为 ,然后违反了该承诺。这感觉像是一个 XY 问题void*
2赞 Alan Birtles 10/17/2023
回报总是一个或多个吗?你能回来吗?floatstd::vector<float>
1赞 Remy Lebeau 10/17/2023
@KapibarovichKapibarov 派生类中重写方法的返回类型必须与要重写的基类方法的返回类型相同或协变(又称派生自)。没有其他选择。因此,您需要修复您的设计以返回所有相关类都可以同意的内容。

答:

2赞 Amit 10/17/2023 #1

如果你想要一个可以返回 ANY TYPE 的函数,一个选项是执行如下操作:

#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());
}
  • 这种解决方案通常是一个糟糕的选择,通常源于糟糕的设计选择。尽管如此,它仍然是一个可行的选择,特别是在它不是由糟糕的设计选择引起的情况下。

--

对于一组 TYPES 这是更好的选择:

#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());
}

评论

2赞 user4581301 10/17/2023
请注意,这几乎总是一个由设计错误导致的坏主意。如果你必须这样做,请停下来想一想是什么把你带到了这里。
1赞 Remy Lebeau 10/17/2023
std::variant在这个例子中,肯定是比std::any
0赞 Amit 10/17/2023
@RemyLebeau,首先,我同意。但是,OP 要求任何类型。std::variant 用于预定义的类型集。无论如何,我还添加了解决方案。std::variant
0赞 Thomas Matthews 10/17/2023
恕我直言,更好的方法是通过参数返回值。这允许将函数重载作为替代方法。