【问题标题】:Decltype of derived class from unique pointer to base class从唯一指针到基类的派生类的decltype
【发布时间】:2020-02-28 10:41:27
【问题描述】:

我想做运行时多态性,并且必须知道实际类型,例如计算某个派生类的实例。这是我目前所拥有的:

标题

struct Base {
  virtual ~Base();
};

struct DerivedFoo : public Base {};
struct DerivedBar : public Base {};

来源

Base::~Base() {}

int main() {
  std::vector<std::unique_ptr<Base>> my_array{};
  my_array.emplace_back(std::make_unique<DerivedFoo>());
  my_array.emplace_back(std::make_unique<DerivedBar>());

  for (const auto& elem : my_array) {
    if (std::is_same<DerivedFoo, decltype(elem)>::value) {
      // do something
    }

    // some testing
    std::cout << typeid(decltype(elem)).name() << '\n';
    std::cout << typeid(decltype(*elem)).name() << '\n';
    std::cout << typeid(decltype(*elem.get())).name() << '\n';
  }

  return 0;
}

输出

St10unique_ptrI4BaseSt14default_deleteIS0_EE
4Base
4Base
St10unique_ptrI4BaseSt14default_deleteIS0_EE
4Base
4Base

问题是:我只设法获得基类的类型。不是派生的。所以我从不输入 if 语句。

  • 我可以添加一个虚拟函数“GetID()”或类似的。但我的感觉是,这是多余的。我应该可以使用类型特征来做到这一点?
  • 我查看了std::unique_pointer&lt;...&gt;::element_typestd::pointer_traits,但无法运行。

有什么想法吗?

【问题讨论】:

  • 真正想做什么?为什么需要检查元素是否属于一种类型?做这样的事情通常是糟糕设计的标志。你的问题不能通过自然多态和虚函数来解决吗?
  • decltype(elem) 返回 unique_ptr 的类型
  • @Someprogrammerdude 我正在编写一个国际象棋引擎。我的基地是一个“方形”类。 “King”、“Queen”、.. 和“Empty”是派生类。一个额外的“Board”类拥有一个成员std::array&lt;std::unique_ptr&lt;Square&gt;, 64&gt; squares_;。我被困在非常基本的单元测试中,以确保在默认构建我的电路板时所有方块都是“空”类型。
  • @Someprogrammerdude 我越想越觉得你是对的。也许我应该简单地添加一个“IsEmpty()”虚函数。不过,仍然对发布的问题的答案感到好奇。
  • 单元测试是一种特殊情况,有时正常的“规则”必须稍微弯曲(有时相当多)。 mocking 可能是一个可能的(并且比规则弯曲更好的)解决方案?

标签: c++ inheritance polymorphism unique-ptr decltype


【解决方案1】:

使用访问者模式

struct Counter;
struct Foo;
struct Bar;
struct BarBar;

struct Counter {
    int barCount = 0;
    int barBarCount = 0;

    void visit(Bar *bar) {
        barCount += 1;
    }
    void visit(BarBar *bar) {
        barBarCount += 1;
    }
};

struct Foo {
    virtual void accept(Counter* v) = 0;
};

struct Bar : public Foo {
    void accept(Counter* v) override {
        v->visit(this);
    }
};

struct BarBar : public Foo {
    void accept(Counter* v) override {
        v->visit(this);
    }
};


int main()
{
    std::vector<std::unique_ptr<Foo>> foos;
    foos.emplace_back(std::make_unique<Bar>());
    foos.emplace_back(std::make_unique<BarBar>());

    Counter counter;

    for(const auto& foo : foos) {
        foo->accept(&counter);
    }

    std::cout << counter.barCount << " " << counter.barBarCount << std::endl;
}

输出1 1

【讨论】:

    【解决方案2】:

    您的代码中的问题是您期望运行时多态性,但只使用编译时类型:

    • decltype(x) 给你 compile-time 类型的 x
    • 所以typeid(decltype(x))指的是编译时类型的运行时类型信息,所以在你的例子中4Base

    如果要使用运行时类型,直接在对象上使用typeid,如下:

    std::cout <<< typeid(elem).name() << '\n';
    std::cout << typeid(*elem).name() << '\n';
    std::cout << typeid(*elem.get()).name() << '\n';
    

    结果将如下所示:

    St10unique_ptrI4BaseSt14default_deleteIS0_EE
    10DerivedFoo
    10DerivedFoo
    St10unique_ptrI4BaseSt14default_deleteIS0_EE
    10DerivedBar
    10DerivedBar
    

    同理std::is_same&lt;DerivedFoo, decltype(elem)&gt;::value是基于模板类型推导的,所以再次编译。如果要在运行时检查多态类型,则需要使用以下构造:

    if (dynamic_cast<DerivedFoo*>(&*elem)) {
        // do something
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-25
      • 1970-01-01
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多