所以,这个问题可能有点令人困惑,但让我来解释一下。
我有一个包含3个其他类和一个向量的类。3个类中的2个继承自第三个类,矢量填充shared_ptr
class top_class
{
class Base
{};
class Derived1 : public Base
{};
class Derived2 : public Base
{};
std::vector<std::shared_ptr<Base>> my_vector;
};
我想创建一个下标运算符重载,根据请求的元素类型,返回对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。
您不能基于某些运行时状态返回变量类型。您可以要求用户提供他想要的类型并使用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]);
}
如果你想抛出这个,你可以检查结果是否为null,并根据情况抛出:
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;
如果不执行动态强制转换,就无法从中获取派生类型的对象。
一旦引入了动态强制转换,就需要考虑动态强制转换的失败。在这一点上,您必须在设计/实现中考虑以下因素。
当动态强制转换失败时,您是想抛出异常,还是想返回与
nullptr
等效的内容?您希望如何调用该函数?派生类型必须作为模板参数传递。如果您选择使用
operator[]
,则需要使用调用它auto ret = obj.operator[]<DerivedType>(index);
这种语法在我看来很难看。也许像
std::vector::at
这样的语法会更合适。auto ret = obj.at<DerivedType>(index);
无论哪种情况,函数的返回类型都将取决于您希望如何处理失败。
如果你选择抛出异常,你需要这样的东西:
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;
}
如果您选择在动态强制转换失败时返回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++中的返回类型是在编译时指定的,所以有点不寻常的语法
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<>
知道返回什么时)。使用原始语法,top_class::operator[]
必须返回可以转换为Derived1
或Derived2
的内容。就我所见,唯一的可能性是Base
,如果Derived1
和Derived2
都允许从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;
};