class 的下标运算符重载,其基数 classes 的向量必须 return Derived classes

Subscript operator overload of a class with a vector of Base classes that has to return Derived classes

所以,这个问题可能有点令人困惑,但让我解释一下。

我有一个 class,其中包含 3 个其他 classes 和一个向量。 3个class中的2个继承自第三个,向量中填充了shared_ptr

class top_class
{
     class Base
     {};

     class Derived1 : public Base
     {};

     class Derived2 : public Base
     {};

std::vector<std::shared_ptr<Base>> my_vector;
};

我想创建一个下标运算符重载 returns 对 Derived1 或 Derived2 的引用,具体取决于我所要求的元素类型。

我一直在尝试使用 auto,但我对 c++11 还是个新手,不太了解它的工作原理。

请记住,我被要求以用户应该这样调用下标运算符的方式提供它:

top_class tc;
Derived1 d1 = tc[ 0 ];
Derived2 d2 = tc[ 1 ];

因此使用模板化函数不是解决方案。我目前的尝试是这样的:

auto operator[](int index) -> decltype(*my_vector[index])
{
    return *my_vector[index];
}

但这当然不起作用,因为我传递给 decltype 的类型是 Base。

您不能 return 基于某些运行时状态的变量类型。您可以做的是要求用户提供他想要的类型并使用 std::dynamic_pointer_cast:

template <class Derived>
std::shared_ptr<Derived> get(size_t idx) const
{
    return std::dynamic_pointer_cast<Derived>(my_vector[idx]);
}

如果你想让这个抛出,你可以检查结果是否为空,并在适当的情况下抛出:

template <class Derived>
std::shared_ptr<Derived> get(size_t idx) const
{
    auto res = std::dynamic_pointer_cast<Derived>(my_vector[idx]);
    if (!res) throw ...;
    return res;
}

到那个时候,我们不妨也用vector::at()扔到那里。

给定成员变量

std::vector<std::shared_ptr<Base>> my_vector;

如果不执行动态转换,就无法从中获取派生类型的对象。

一旦引入动态转换,您将需要考虑动态转换的失败。届时,您必须在 design/implementation.

中考虑以下因素
  1. 当动态转换失败时,你想抛出异常,还是想return 等同于nullptr的东西?

  2. 您想如何调用函数?派生类型必须作为模板参数传递。如果您选择使用 operator[],则需要使用

    调用它
    auto ret = obj.operator[]<DerivedType>(index);
    

    我觉得那个语法很难看。也许像 std::vector::at 这样的东西会更合适。

    auto ret = obj.at<DerivedType>(index);
    

    无论哪种情况,函数的 return 类型将取决于您希望如何处理失败。

如果您选择抛出异常,您将需要像这样的东西:

template <typename DerivedType>
DerivedType& at(size_t index)
{
   std::shared_ptr<Base> ptr = my_vector.at(index);
   dynamic_cast* derived_ptr = dynamic_cast<DerivedType>(ptr.get());
   if ( derived_ptr == nullptr )
   {
      throw std::bad_cast();
   }
   return *derived_ptr;
}

如果您选择 return 一个 nullptr 当动态转换失败时,您将需要类似的东西:

template <typename DerivedType>
DerivedType* at(size_t index)
{
   std::shared_ptr<Base> ptr = my_vector.at(index);
   return dynamic_cast<DerivedType>(ptr.get());
}

请注意,如果 index 超出范围,std::vector::at 将抛出异常。

由于 C++ 中的 return 类型是编译时指定的,因此有些不寻常的语法

top_class tc;
Derived1 d1 = tc[ 0 ];
Derived2 d2 = tc[ 1 ];

很难实现(更简单的是

Derived1 d1 = tc.get<Derived1>(0);
Derived2 d2 = tc.get<Derived2>(1);

top_class::get<> 知道要 return 时)。使用原始语法,top_class::operator[] 必须 return 可以转换为 Derived1Derived2 的内容。据我所知,唯一的可能性是 Base,如果 Derived1Derived2 都允许从 Base:

构建
struct top_class
{
  struct Base { /* ... */ };
  struct Derived1 : Base
  {
    Derived1(Base const&);
    // ...
  };
  struct Derived2 : Base
  {
     Derived2(Base const&);
    // ...
  };
  Base const&operator[](std::size_t i) const
  {
    return *(data.at(i));
  }
private:
  std::vector<std::shared_ptr<Base>> data;
};