方法 doSomething(vector<ParentClass>) 是否可以根据作为参数传递的向量<ChildClass>类型而采取不同的行为?

问题描述 投票:0回答:3

在参加 C++ 入门课程时,我开始思考以下内容是否可能:

#include <vector>
using namespace std;

class Parent {};
class ChildA : public Parent {};
class ChildB : public Parent {};

void doSomething(vector<Parent> v){
  if (v is of type vector<ChildA>) ...
  else if (v is of type vector<ChildB>) ...
}

int main(void){
  vector<ChildA> a;
  vector<ChildB> b;
  doSomething(a);
  doSomething(b);
  return 0;
}

简而言之,我只想定义一次的方法

doSomething()
会根据输入向量是否具有
ChildA
ChildB
类型的元素而表现不同。目前我面临两个问题:

  1. 采用
    Parent
    作为参数的方法也允许我将
    ChildA
    作为参数传递,但一旦使用向量,情况就不是这样了,即我收到错误

no suitable user-defined conversion from "std::vector<ChildA, std::allocator<ChildA>>" to "std::vector<Parent, std::allocator<Parent>>" exists

  1. 这个问题并不严重,但我目前正在通过
    这篇文章
    中的答案找到v的类型,产生诸如
    St6vectorI6ChildASaIS0_EE
    ,这看起来不太优雅。另一方面,我似乎无法理解像this这样的帖子,因为它们涉及模板,而我不知道如何使用模板。
c++ inheritance vector polymorphism
3个回答
1
投票

问题

void doSomething(vector<Parent> v){
  if (v is of type vector<ChildA>) ...
  else if (v is of type vector<ChildB>) ...
}

两个测试的答案都是

false
。正如声明所说,参数
v
是一个
vector<Parent>

你需要

template< typename Elem>
void doSomething(vector<Elem> v){
  if constexpr(std::is_base_of_v<ChildA, Elem>) ...
  else if constexpr(std::is_base_of_v<ChildB, Elem>) ...
}

现在您有了实际的

Elem
类型并且可以检查它。但简单的重载可能会更容易。


0
投票

您可以依靠动态多态性、静态多态性或两者的混合来实现此目的。

动态多态性:

void do_something(const vector<Parent*>& vec) {
    for (auto* p : vec) 
        p->call_virtual_function();
}

本例中的向量需要包含指向对象的(智能)指针以避免切片。

过载解析:

void overloaded_function(const Child1& ch) { }
void overloaded_function(const Child2& ch) { }

template<class Type, std::enable_if_t<std::is_base_of:v<Parent, Type>, int> = 0> // or c++20 concepts
void do_something(const vector<Type>& vec) {
    for (auto& el : vec)
        call_overloaded_function(el);
}

在这种情况下,向量必须是

Child1
Child2
类型的向量才能工作,但请注意,在这种情况下,向量不需要包含指针。这不依赖于动态多态性。

您也可以使用

dynamic_cast
但这可能被视为糟糕的代码:

void do_something_single(const Parent* p)
{
    if (auto* c = dynamic_cast<const Child1*>(p)) {
        // do something with c as Child1
    } else if (auto* c = dynamic_cast<const Child2*>(p)) {
        // do something with c as Child2
    } else ...
}

void do_something(const vector<Parent*>& vec) {
    for (auto*p : vec) {
        do_something_single(p);
    }
}

0
投票

你所要求的是不可能的。

std::vector<Parent>
std::vector<ChildA>
std::vector<foo>
std::vector<bar>
一样有很多共同点:几乎没有。它们是同一模板的实例化,但这意味着比您想象的要少,特别是
std::vector<ChildA>
std::vector<ChildB>
没有共同的基类。

您需要多态性的指针或引用。也许您实际上想要一个

std::vector<std::unique_ptr<Parent>>
可以存储指向三种类型之一的指针。

对于对象向量,您可以将函数设为函数模板:

 template <typename T>
 void doSomething(std::vector<T> v){ /*...*/ }

但是,如果您希望

doSomething
根据类型做不同的事情,那么这样做几乎没有什么好处,您可以简单地使用重载:

 void doSomething(std::vector<ChildA>) { /*...*/ }
 void doSomething(std::vector<ChildB>) { /*...*/ }

采用 Parent 作为参数的方法也允许我将 ChildA 作为参数传递,[...]

是的,但不是。方法

void foo(Parent)
仅接受
Parent
对象作为参数。如果您向其传递
ChildA
,则派生类的部分将被切掉,并且仅将
Parent
传递给函数。请参阅什么是对象切片?。您需要多态性的指针或引用。

© www.soinside.com 2019 - 2024. All rights reserved.