c++ 静态多态和模板容器

smdncfj3  于 2023-04-01  发布在  其他
关注(0)|答案(2)|浏览(145)

我试图使用静态多态和模板来创建一个可以容纳更多类型的容器,从我对模板的了解来看,这是不可能的,但我希望我错了,有一种方法。我有以下类:

template<class Derived>
class base
{
public:
    base(string);
    void clean()
    {
        cout << "I'm cleannig \n";
    }
    void process()
    {
        static_cast<Derived*>(this)->setup();
        static_cast<Derived*>(this)->run();
        static_cast<Derived*>(this)->cleanup();
    }
    string name;
};

template<class Derived>
base<Derived>::base(string y):name(y)
{
}

class derived : public  base<derived> 
{
    friend class base<derived>;
    void setup() {cout << "derived setup \n"; }
    void run() { cout << "derived run \n"; }
    void cleanup() { cout << "derived cleanup \n"; }

};

class derived1 : public base<derived1> 
{
    friend class base<derived1>;
    void setup() {cout << "derived1 setup \n"; }
    void run() { cout << "derived1 run \n"; }
    void cleanup() { cout << "derived1 cleanup \n"; }
};

我想创建一个可以容纳它们的容器,我尝试了这个代码-

template <class T>
class Y{
 public:
 std::vector<base<T>> m_vec;   

};

template <typename T>
class D:public Y<T>
{
    public:
    friend class Y<T>;
    void print()
    {
        for(auto& e: Y<T>::m_vec)
        {
            e.process();
        }
    } 
};

int main()
{ 
    base<derived>* b =  new base<derived>;
    base<derived1>* c =  new base<derived1>;

    D<derived> y;
    y.m_vec.push_back(b);
    y.m_vec.push_back(c);
    y.print();
}

但它不工作,我试图这样做:

y.m_vec.push_back(static_cast<base<derived>>(c));

我得到这个错误:
错误:没有匹配函数用于调用'std::vector,std::allocator〉〉::push_back(base*&)' y.m_vec.push_back(b);

bnl4lu3b

bnl4lu3b1#

经过一些测试和挖掘,答案是没有办法做到这一点。但是你可以使用std::any,如@formerlyknownas_463035818建议的,将std::vector声明为:

std::vector<std::any> m_vec;

代替

std::vector<base<T>> m_vec;

然后使用boost demangle函数来获取类型-

std::string name(boost::core::demangle(e.type().name()));

然后使用某种工厂函数将any_cast转换为您需要的类型

if(!name.compare("base<derived1>*") )
 {
     try {
             auto* r = any_cast<base<derived1>*>(e);
             r->process();
         }
         catch(const std::bad_any_cast& e) {
             std::cout << e.what() << '\n';
         }
  }
  else
  {
     try {
         auto *r = any_cast<base<derived> *>(e);
         r->process();
     }
     catch(const std::bad_any_cast& e) {
         std::cout << e.what() << '\n';
     }
   }

或者不使用demangle name和use string compare,你可以使用any类的type()函数,并像这样比较typeid:

if(e.type()==typeid(base<derived1>*))
m528fe3b

m528fe3b2#

只要在调用process()函数时没有关于实际类型的信息,这是不可能的。
从C++17开始,你可以使用std::variant来存储类型信息。你可以让process成为一个静态函数,它的参数是从base派生的类的示例。

#include <iostream>
#include <string>

class Base {
public:
    Base(std::string name): name_(std::move(name)) {}

    template <typename T>
    static void process(T& self) {
        std::cout << "process " << self.name_ << '\n';
        self.run();
    }

protected:
    std::string name_;
};

struct A: Base {
    using Base::Base;
    void run() { std::cout << "run A " << name_ << '\n'; }
};

struct B: Base {
    using Base::Base;
    void run() { std::cout << "run B " << name_ << '\n'; }
};

然后你可以创建一个std::vectorstd::variantAB。通过visit语句中的泛型lambda调用process()
x一个一个一个一个x一个一个二个x
使用C++23可以简化得更多。

import std;

class Base {
public:
    Base(std::string name): name_(std::move(name)) {}

    void process(this std::derived_from<Base> auto& self) {
        std::print("process {}\n", self.name_);
        self.run();
    }

protected:
    std::string name_;
};

struct A: Base {
    using Base::Base;
    void run() { std::print("run A {}\n", name_); }
};

struct B: Base {
    using Base::Base;
    void run() { std::print("run B {}\n", name_); }
};

int main() {
    std::vector<std::variant<A, B>> array{A{"Cat"}, B{"Dog"}};
    for (auto& element: array) {
        std::visit([](auto& a_or_b) {
            a_or_b.process();
        }, element);
    }
}

有四点不同:
1.包含已被C++23 moduleimport std取代。

  1. std::cout已被std::print取代。
  2. process现在是一个普通的非静态成员函数,在新的C++23表示法中具有显式的this。它仍然是一个模板,但受到C++20 concept的约束。
    1.调用现在使用普通的成员函数调用语法a_or_b.process();

相关问题