我想dynamic_cast
模板派生类型,模板参数未知:
struct A {
virtual void f() { };
};
template <std::size_t N>
struct B : public A { };
使用已知的模板参数,强制转换可以按以下方式执行:
const A& base_ref = B<N>();
const B<N>& ref = dynamic_cast<const B<N>&>(base_ref);
我的问题是关于模板参数(N
(的值未知的情况。
我想知道是否有办法获取对象类型,在这种情况下B<N>
从基类指针/引用中获取对象类型,以便可以自动推断dynamic_cast模板参数?
附言在所考虑的情况下,模板参数是整数类型,用作std::array
的模板参数。
如果你只有几个可能的N
值,那么你可以尝试对每个值dynamic_cast
'ing。否则,答案是:
不。作为编译时常量,没有。如果您不需要编译时常量,则可以执行以下操作:
struct AWithN {
int n;
AWithN(int n_) : n(n_) { }
};
template <std::size_t N>
struct B: public AWithN {
B() : AWithN(N) { }
};
你可以dynamic_cast
到AWithN,从那里你可以得到n
.
好吧,这会很讨厌。
在以下情况下,您可以自动查找有效的动态转换:
-
您将
B<N>
中N
的最大值限制为编译器满意的值。 -
您提供采用模板参数的访问者,以便对成功转换的结果进行操作。
-
您不介意顺序查找(尽管此处[1]的进一步工作将涉及构建成功查找的缓存以改善运行时(。
[1] 进一步的工作显然是浪费时间。时间最好花在更好的设计上。
#include <type_traits>
#include <vector>
#include <iostream>
#include <utility>
#include <array>
#include <algorithm>
#include <stdexcept>
struct A
{
virtual ~A() = default;
};
template<std::size_t N>
struct B : A
{
};
template<std::size_t I>
struct cast_test
{
static bool test(A* p) {
return dynamic_cast<B<I>*>(p) != nullptr;
}
};
template<std::size_t...Is>
constexpr auto make_cast_tests(std::index_sequence<Is...>)
{
return std::array<bool(*)(A*), sizeof...(Is)> {
&cast_test<Is>::test...
};
}
template<class Visitor, std::size_t I>
struct caller
{
// figuring out a common return type is a whole new challenge...
static void call(Visitor& visitor, A* p) {
return visitor(static_cast<B<I>*>(p));
}
};
template<class Visitor, std::size_t...Is>
constexpr auto make_callers(std::index_sequence<Is...>)
{
return std::array<void(*)(Visitor&, A*), sizeof...(Is)> {
&caller<Visitor, Is>::call...
};
}
template<std::size_t N, class Visitor, class sequence_type = std::make_index_sequence<N>>
decltype(auto) dynamic_visit(Visitor&& visitor, A*p)
{
constexpr auto tests = make_cast_tests(sequence_type());
auto ipos = std::find_if(std::begin(tests), std::end(tests), [&p](auto&& f){ return f(p); });
if (ipos == std::end(tests))
{
throw std::logic_error("increase the range of N");
}
constexpr auto callers = make_callers<Visitor>(sequence_type());
auto icaller = std::begin(callers) + std::distance(std::begin(tests), ipos);
return (*icaller)(visitor, p);
}
struct visitor
{
template<std::size_t I>
void operator()(B<I>* p) const
{
std::cout << "I is " << I << std::endl;
}
};
auto make_an_A(int argc) -> A*
{
if (argc > 1) {
return new B<6>;
}
else {
return new B<7>;
}
}
int main(int argc, char** argv)
{
A* p = make_an_A(argc);
dynamic_visit<100>(visitor(), p);
}
使用 ./a.out 调用时的预期结果:
I is 7
使用 ./a.out "foo" 调用时的预期结果:
I is 6