dynamic_cast到具有未知模板参数的派生类型



我想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.

好吧,这会很讨厌。

在以下情况下,您可以自动查找有效的动态转换:

  1. 您将B<N>N的最大值限制为编译器满意的值。

  2. 您提供采用模板参数的访问者,以便对成功转换的结果进行操作。

  3. 您不介意顺序查找(尽管此处[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

最新更新