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

时间:2017-07-04 10:52:38

标签: c++ templates

我想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的模板参数。

2 个答案:

答案 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

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

  2. 您提供了一个访问模板参数的访问者,以便对成功投射的结果进行操作。

  3. 您不介意顺序查找(尽管此处[1]的进一步工作将涉及构建成功查找的缓存以改善运行时间。)

  4. [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