我想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模板参数?
P.S。在所考虑的情况下,template参数是整数类型,并用作std::array
的模板参数。
答案 0 :(得分:1)
如果您只有N
的几个可能值,那么您可以尝试dynamic_cast
每个值。否则,答案是:
没有。作为编译时常量,没有。如果您不需要编译时常量,那么您可以这样做:
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 :(得分:1)
好的,这将是令人讨厌的。
您可以自动查找有效的动态广告, if :
您将N
中B<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