我有一个有两个子类的Base
。bar
创建了一个类型为Base
的unique_ptr
,并尝试用它的一个子类初始化它。
当然,我不能在不显式指定类型的情况下进行向下转换,这是我不想做的。有什么方法可以绕过它吗?
struct Base
{
};
struct A : public Base
{
int val;
};
struct B : public Base
{
int val;
};
struct C : public Base
{
// does not have val
};
void bar(bool x, int value)
{
std::unique_ptr<Base> ptr;
if (x)
{
ptr = std::make_unique<A>();
}
else
{
ptr = std::make_unique<B>();
}
ptr->val = value; // ERROR
}
c++中的类型不是这样工作的。
如果*ptr
需要val
,这必须反映在静态中*ptr
. type
在您的示例中,*ptr
的静态类型是Base
。这里没有val
。
*ptr
总是有val
,但这无关紧要。该实现不做数据流分析。它只查看静态类型。
如果你需要Base
的一些后代有val
而其他的没有,创建一个中间类BaseWithVal
并使用它。
您可以使用通用的lambda或helper函数模板来避免代码重复:
void bar(bool x, int value)
{
auto make_ptr = [&]<typename T>(){
auto ptr = std::make_unique<T>();
ptr->val = value;
return ptr;
};
std::unique_ptr<Base> ptr;
if (x)
{
ptr = make_ptr.operator()<A>();
}
else
{
ptr = make_ptr.operator()<B>();
}
}
对于lambda上的显式模板形参,这需要c++ 20。在c++ 20之前,辅助函数更容易管理,尽管下面的替代方法也可以工作:
template<typename T>
struct type_identity {
using type = T;
};
void bar(bool x, int value)
{
auto make_ptr = [&](auto t){
using T = typename decltype(t)::type;
auto ptr = std::make_unique<T>();
ptr->val = value;
return ptr;
};
std::unique_ptr<Base> ptr;
if (x)
{
ptr = make_ptr(type_identity<A>{});
}
else
{
ptr = make_ptr(type_identity<B>{});
}
}
但是正如另一个答案所说,如果需要统一访问具有val
成员的派生类子集,那么这些子类很可能与该成员共享一个中间基类。这样,您可以简单地将std::unique_ptr<Base>
替换为std::unique_ptr<IntermediateBase>
,一切都将正常工作。