我想让局部变量的运行时类型取决于某些条件。假设我们有这种情况:
#include <iostream>
class Base{
public:
virtual void foo()=0;
};
class Derived1 : public Base {
virtual void foo(){
std::cout << "D1" << std::endl;
}
};
class Derived2 : public Base {
virtual void foo(){
std::cout << "D2" << std::endl;
}
};
在类Java语言中,对象总是通过"引用"处理,解决方案很简单(伪代码(:
Base x = condition ? Derived1() : Derived2();
C++解决方案显然会涉及指针(至少在幕后(,因为没有其他方法可以将两种不同的类型放在同一个变量(必须有一个类型(下。它不能简单地Base
因为Base
对象不能被构造(它有一个纯虚函数(。
最简单的方法是使用原始指针:
Base* x = condition ? static_cast<Base*>(new Derived1()) : static_cast<Base*>(new Derived2());
(需要强制转换才能使三元运算符的两个分支具有相同的类型(
手动指针处理容易出错且老派,这种情况需要unique_ptr
。
std::unique_ptr<Base> x{condition ? static_cast<Base*>(new Derived1()) : static_cast<Base*>(new Derived2())};
啊。。。不完全是我所说的优雅。它使用显式新建和强制转换。我希望使用类似std::make_unique
的东西来隐藏new
但这似乎是不可能的。
这只是你得出结论"C++就是这样,如果你需要优雅,使用其他语言(也许在其他方面做出权衡("的情况之一?
还是这整个想法完全不C++?我在这里的心态是错误的,试图将不同语言的想法强加给C++吗?
这只是你得出结论"C++就是这样,如果你需要优雅,使用其他语言(也许在其他方面做出权衡("的情况之一?
还是这整个想法完全不C++?我在这里的心态是错误的,试图将不同语言的想法强加给C++吗?
这实际上取决于您将使用x
做什么。
变种
C++解决方案显然会涉及指针(至少在幕后(,因为没有其他方法可以将两种不同的类型放在同一个变量下(必须有一个类型(。
您也可以使用 boost::variant
(或boost::any
,但在这种情况下boost::variant
可能会更好(。例如,假设Derived1
是默认可构造的:
boost::variant<Derived1, Derived2> x;
if (!condition) x = Derived2();
即使Derived1
和Derived2
不共享基类,这也将起作用。然后,您可以使用访客模式对x
进行操作。例如,给定:
struct Derived1 {
void foo1(){
std::cout << "D1" << std::endl;
}
};
struct Derived2 {
void foo2(){
std::cout << "D2" << std::endl;
}
};
然后,您可以将访问者定义为:
class some_visitor : public boost::static_visitor<void> {
public:
void operator()(Derived1& x) const {
x.foo1();
}
void operator()(Derived2& x) const {
x.foo2();
}
};
并将其用作:
boost::apply_visitor(some_visitor(), x);
现场演示
多态调用
如果您真的需要多态地使用x
,那么是的,std::unique_ptr
都可以。只需将您的多态函数称为x->foo()
:
std::unique_ptr<Base> x = condition ? std::unique_ptr<Base>(new Derived1()) : std::unique_ptr<Base>(new Derived2());
现场演示
概念/模板
如果你只需要调用一个函数,那么定义一个概念并用模板表达它可能更好:
template<class Type>
void my_func(Type& x) { x.foo(); }
您也可以在将来的C++版本中明确定义概念。
现场演示
一种"激进"的可能性是创建一种新的make_unique,它将创建正确的类型返回值
template<typename TReal, typename TOutside, typename... Args>
auto make_base_unique(Args&&... args) -> std::unique_ptr<TOutside>
{
return std::unique_ptr<TOutside>(new TReal(std::forward<Args>(args)...));
}
然后像这样使用它:
auto x = (condition ? make_base_unique<Derived1,Base>() : make_base_unique<Derived2,Base>());