c++ 如何模拟“虚拟变量函数”的部分重写?

46qrfjad  于 2023-05-30  发布在  其他
关注(0)|答案(3)|浏览(264)

首先,我知道变量函数在c++中不能是虚的。我的问题是如何模仿下一个“不正确”的例子。我希望有一个类A和类B,它继承了它,只实现了“它的一部分”:

class A {
template<class... Types>
virtual void f(Types... buffers) { return; }
};

class B : public A {
// Overrides only 2 options of "generic" f
void f(unsigned char* buff2) override;
void f(unsigned char* buff1, unsigned int* buff2) override;
};

在我的用例中,有这个通用的f调用非常重要,但是不同的类只支持可变函数f的“子集”:

A* a= factory(...);
a->f(buff1, buff2, buff3); // OK
a->f(buff1); // OK
a->f(buff1, buff2); // Error! Factory gave an instance which does not support this call
vpfxa7rd

vpfxa7rd1#

你可以使用std::variant代替virtual,并使用重载作为分派:

class A {};
class B : public A {};

template<class... Types>
void f(A&, Types... buffers) { std::cout << "A" << sizeof...(buffers) << std::endl; }

void f(B&, unsigned char* buff1) { std::cout << "B1\n"; }
void f(B&, unsigned char* buff1, unsigned int* buff2) { std::cout << "B2\n"; }

然后呢

std::variant<A, B> vars[] =  {A{}, B{}};

unsigned char buff1[42]{};
unsigned int buff2[42]{};

for (auto& var : vars) {
    std::visit([&](auto& a_or_b) { f(a_or_b, buff1); }, var);
    std::visit([&](auto& a_or_b) { f(a_or_b, buff1, buff2); }, var);
    std::visit([](auto& a_or_b) { f(a_or_b); }, var);
}

Demo

tmb3ates

tmb3ates2#

由于各种f函数除了它们的名称之外没有任何共同之处,所以我将它们建模为实际的独立接口,这与现有的OOP工具更好地结合在一起,这些工具可以处理可选地实现接口(即dynamic_cast)。A和它自己的f模板只是隐藏了交叉转换和错误处理。

template <class... Types>
struct HasF {
    virtual void f(Types... buffers) = 0;
};

struct A {
    virtual ~A() = default;

    template <class... Types>
    void f(Types... buffers) {
        auto *const p = dynamic_cast<HasF<Types...>*>(this);

        if(!p) {
            // Error: the dynamic type cannot handle these arguments.
        }
        
        p->f(buffers...);
    }
};

struct B
: A
, HasF<unsigned char *>
, HasF<unsigned char *, unsigned int *> {
    void f(unsigned char* buff2) override { /* ... */ }
    void f(unsigned char* buff1, unsigned int* buff2) override { /* ... */ }
};

See it live on Godbolt.org

3pmvbmvn

3pmvbmvn3#

你好像在找std::any

class A {
public:
template<class... Types>
void f(Types... buffers) { return f_impl(std::tuple(buffers...)); }
private:
virtual void f_impl(std::any arg) = 0;
};

class B : public A {
// Overrides only 2 options of "generic" f
void f_impl(std::any arg) override
{
    if (auto * tup = std::any_cast<std::tuple<unsigned char*>>(&arg)) {
        // single arg case
    } else if (auto * tup = std::any_cast<std::tuple<unsigned char*, unsigned char*>>(&arg)) {
        // two arg case
    } else {
        throw std::runtime_error("unsupported call"); // or whatever
    }
};

然而,您的需求意味着一个可怕的设计问题。如果不知道A实际上是什么子类,那么给定A的人就不知道他们可以用它做什么,此时他们应该有对该类型的引用。

相关问题