一直以来,我发现自己都是这样做的:
Animal *animal = ...
if (Cat *cat = dynamic_cast<Cat *>(animal)) {
...
}
else if (Dog *dog = dynamic_cast<Dog *>(animal)) {
...
}
else { assert(false); }
一旦我看到C ++ 11中的闭包,我想知道,这样的事情可能吗?
Animal *animal = ...
typecase(animal,
[](Cat *cat) {
...
},
[](Dog *dog) {
...
});
实现类型注释本来应该很容易,但我一直遇到一个问题,它无法弄清楚函数的参数,所以它无法知道尝试使用dynamic_cast,因为很难推断出lambdas的参数。花了几天的时间搜索google和SO,但终于想通了,所以我将在下面分享我的答案。
答案 0 :(得分:10)
感谢ecatmur在https://stackoverflow.com/a/13359520的回答,我能够从lambda中提取签名。完整的解决方案如下所示:
// Begin ecatmur's code
template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };
template<typename T>
struct get_signature_impl { using type = typename remove_class<
decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;
// End ecatmur's code
// Begin typecase code
template<typename Base, typename FirstSubclass, typename... RestOfSubclasses>
void typecase(
Base *base,
FirstSubclass &&first,
RestOfSubclasses &&... rest) {
using Signature = get_signature<FirstSubclass>;
using Function = std::function<Signature>;
if (typecaseHelper(base, (Function)first)) {
return;
}
else {
typecase(base, rest...);
}
}
template<typename Base>
void typecase(Base *) {
assert(false);
}
template<typename Base, typename T>
bool typecaseHelper(Base *base, std::function<void(T *)> func) {
if (T *first = dynamic_cast<T *>(base)) {
func(first);
return true;
}
else {
return false;
}
}
// End typecase code
以及一个示例用法:
class MyBaseClass {
public:
virtual ~MyBaseClass() { }
};
class MyDerivedA : public MyBaseClass { };
class MyDerivedB : public MyBaseClass { };
int main() {
MyBaseClass *basePtr = new MyDerivedB();
typecase(basePtr,
[](MyDerivedA *a) {
std::cout << "is type A!" << std::endl;
},
[](MyDerivedB *b) {
std::cout << "is type B!" << std::endl;
});
return 0;
}
如果有人有任何改进,请告诉我!
答案 1 :(得分:3)
template <typename T, typename B>
void action_if(B* value, std::function<void(T*)> action)
{
auto cast_value = dynamic_cast<T*>(value);
if (cast_value != nullptr)
{
action(cast_value);
}
}
Animal* animal = ...;
action_if<Cat>(animal, [](Cat* cat)
{
...
});
action_if<Dog>(animal, [](Dog* dog)
{
...
});
我无法在第二次访问C ++ 11编译器来尝试这一点,但我希望这个想法很有用。根据编译器能够进行多少类型推断,您可能需要或不必指定两次大小写的类型 - 我不是C ++ 11-pro就足以告诉它。
答案 2 :(得分:3)
前段时间我正在尝试编写一个库来完成这项工作。
你可以在这里找到它:
https://github.com/nicola-gigante/typeswitch
这个项目非常雄心勃勃,有很多计划的功能,而且还需要完成(还有一个我已经知道的重要错误,但在这几个月我没有时间再研究了它) 。但是,对于经典的类层次结构的用例,它可以完美地工作(我认为)。
基本机制与之前发布的基本机制相同,但我尝试使用更多功能扩展概念:
T
是所有子句返回的类型的通用类型。如果没有默认大小写,则返回类型为optional<T>
而不是T
。boost::optional
或std::experimental::optional
的任何实现(例如,libc++
提供一个)。 boost::any
对象中包含的类型。dynamic_cast
。这在使用提供自己的转换基础结构的库(例如Qt的qobject_cast
)或在您自己的标记联合或类似的事情上实现typeswitch时非常有用。库已经完成了,除了README
中记录的一个错误,它使得非多态类型与静态重载解析规则无法匹配,但它只是在模板通用中有用的情况代码,并不涉及像你这样的大多数用例。我目前没有时间去处理它来修复bug,但我想最好在这里发布它而不是让它不用。
答案 3 :(得分:2)
我认为这取决于您是否希望在编译时或运行时执行此操作。在编译时,Verdagon的答案更好,在运行时你可以做这样的事情
class A {
};
class B {
};
void doForA() {
std::cout << "I'm A" << std::endl;
}
void doForB() {
std::cout << "I'm B" << std::endl;
}
int main() {
std::unordered_map<std::type_index, std::function<void(void)>> mytypes;
mytypes[typeid(A)] = doForA;
mytypes[typeid(B)] = doForB;
mytypes[typeid(A)]();
}
但两种方式都错了virtual
关键字在这里,你必须像Arie所说的那样或者你的架构中存在错误
答案 4 :(得分:1)
我认为你实际上想要使用继承而不是typecasing(我以前从未见过它,非常整洁:))或类型检查。
小例子:
class Animal {
public:
virtual void makeSound() = 0; // This is a pure virtual func - no implementation in base class
};
class Dog : public Animal {
public:
virtual void makeSound() { std::cout << "Woof!" << std::endl; }
}
class Cat : public Animal {
public:
virtual void makeSound() { std::cout << "Meow!" << std::endl; }
}
int main() {
Animal* obj = new Cat;
obj->makeSound(); // This will print "Meow!"
}
在这种情况下,我想要打印特定于动物的声音而不进行特定的类型检查。为此,我使用虚拟功能&#34; makeSound&#34; Animal的每个子类都有并覆盖以打印该动物的正确输出。
希望这是你的目标。