【问题标题】:How to dynamic_cast a variadic template with mismatch reference modifier in parameters?如何动态转换参数中具有不匹配引用修饰符的可变参数模板?
【发布时间】:2015-10-04 17:31:03
【问题描述】:

我有一个类层次结构,其中基类包含一个函数,该函数根据接收到的参数测试dynamic_cast 指向派生模板类型的this 指针。

问题是dynamic_cast 运算符检查参数是否完全匹配,并考虑它们是否为 ref。我需要以下示例来处理这两种情况,有没有办法这样做?

template <typename... P>
struct Derived;

struct Base
{
    virtual ~Base() {}

    template <typename... P>
    void doCast(P&&... p) {
        Derived<P...> *res=dynamic_cast<Derived<P...> *>(this);
        if (!res)
            std::cout<<"Failed."<<std::endl;
        else {
            std::cout<<"Success."<<std::endl;
            res->doSomethingWithP(std::forward<P>(p)...);
        }
    }
};

template <typename... P>
struct Derived: public Base
{
    void doSomethingWithP(P... p) {
        /*Whatever, doesn't matter*/
    }
};

int main(int argc, char **argv)
{
    Derived<int, int, int> derived;
    Base *b=&derived;

    int x=10;
    int y=20;
    int z=30;

    //The two calls must succeed.
    b->doCast(x,y,z); //Failed.
    b->doCast(10,20,30); //Success.

    return 0;
}

Derived 可以接受既是值、引用又是指针的类型 - cv - 限定与否。

【问题讨论】:

  • 要清楚 - 你真的会实例化Derived&lt;int, int&amp;&gt;吗?在这种情况下b-&gt;doCast(10, 20);应该怎么做?
  • 或者说真的,如果Derived&lt;int&gt;Derived&lt;int&amp;&gt; 都可以的话,b-&gt;doCast(x); 应该怎么做?
  • 好的,所以它们可以是对 const 的引用。同样的问题,Derived&lt;int&gt;Derived&lt;int const&amp;&gt;?
  • 如果 this 类是 intconst int &amp; 它应该可以很好地进行两种转换。
  • @LoPiTaL 你不理解这个问题。假设doCast 使用AB、...、E 类型的5 个参数调用。这意味着我们必须潜在地检查 {A, A const&amp;} x {B, B const&amp;} x {C, C const&amp;} x ... 那是 32 个潜在的 Derived 类型!

标签: c++ c++11 variadic-templates dynamic-cast perfect-forwarding


【解决方案1】:

只是将我的 cmets 移动到一个答案。在典型情况下,如果Derived&lt;int&gt;Derived&lt;int const&amp;&gt; 都被允许作为类型,Base::doCast 可能必须对每个参数类型执行两个不同的转换,或者2^N 总转换。这是不可行的。

但是,您可以做的是 (1) 要求 Derived 的模板参数不是引用,并且 (2) 让它通过引用获取其参数。所以我们只删除所有Ps 上的引用:

template <typename... P>
void doCast(P&&... p) {
    if (auto res = dynamic_cast<Derived<std::remove_reference_t<P>...>*>(this)) {
        std::cout << "Success." << std::endl;
        res->doSomethingWithP(p...); // NOT forward
    }
    else {
        std::cout << "Failed." << std::endl;
    }
}

并在另一边引用,这样我们就不会产生额外的副本:

template <typename... P>
struct Derived: public Base
{
    void doSomethingWithP(P&... ) {
        /*Whatever, doesn't matter*/
    }
};

但是,这仍然让我们可以创建Derived&lt;int&amp;&gt;,它永远不会在这个模型中工作。因此,让我们明确禁止这种情况。首先是一些样板:

template<bool...> struct bool_pack;

template<bool f, bool... bs> 
using all_same = std::is_same<bool_pack<bs..., f>, bool_pack<f, bs...>>;

template <bool... bs>
using none = all_same<false, bs...>;

然后:

template <typename... P>
struct Derived: public Base
{
    static_assert(none<std::is_reference<P>::value...>::value, 
                  "Can't make Derived with references");
    ...
};

【讨论】:

  • 它就像一个魅力!非常感谢!顺便说一句,由于我没有使用c++14,我不能使用std::remove_reference_t这个类型,所以为了让typename正确,定义应该如下:Derived&lt;typename std::remove_reference&lt;P&gt;::type... &gt;*
猜你喜欢
  • 2021-01-05
  • 1970-01-01
  • 2023-03-07
  • 2012-12-06
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
相关资源
最近更新 更多