繁体   English   中英

使用好奇递归模板模式在子类之间进行转换

[英]Conversion between sub-classes using Curiously Recursive Template Pattern

鉴于:

  • 具有typename参数value_type的模板类Base
  • 好奇的递归模板模式模板“子类” DerivedFooDerivedBar ,以及
  • 模板“子类” DerivedBarCodeDerivedBarDoge这是源自DerivedBar

我怎么可能,或者我有可能:

  • 如图所示,在三个叶子派生类型的每对之间实现转换(通过显式构造函数,赋值运算符 ),并且
  • 禁用(通过编译器错误消息)图表中未指定的转换?

该图:

Base -+---> DerivedFoo <-------+
      +-+-> DerivedBar         | // Three-way
        +---> DerivedBarCode <-+ // conversion
        +---> DerivedBarDoge <-+

该场景摘自:

MatrixBase -+---> DenseMatrix <------------+
            +-+-> SparseMatrixBase         | 
              +---> MatrixCSR <------------+ // Multiple-way
              +---> MatrixCSC <------------+ // conversion
              +---> MatrixModifiedCSR <----+
              +---> MatrixModifiedCSC <----+

其中每个类至少具有一个公共模板参数typename value_type

最小说明示例:

说明:

  • 第116 ... 128 第160 ... 169行显示了我尝试在模板类之外定义模板类的显式模板构造函数,以将DerivedBarCode<value_type>DerivedBarDoge<value_type>转换为DerivedFoo<value_type>
  • 第49行包含静态断言,该断言会因图中未指定的所有转换而停滞。
  • 删除行189 ... 202以及行116 ... 128行160 ... 169,以成功编译并运行。

码:

#include <iostream>

// ----------------------------------------------------------------------------
// Base
// ----------------------------------------------------------------------------
template <typename value_type, typename derived_type>
class Base
{
public:
    // Pass along the value type.
    typedef value_type value_type;
    // Identify myself in the Curiously Recursive Template Pattern hierarchy.
    typedef Base object_type;
    // Delegate to derived types.
    derived_type & asLeaf() { return static_cast<derived_type &>(*this); }

protected:
    value_type m_Base;

public:
    Base(value_type base = value_type()) : m_Base(base) {}

public:
    // A delegated function
    void Dump() { asLeaf().Dump(); }
};

// ----------------------------------------------------------------------------
// Base -----> Derived Foo
// ----------------------------------------------------------------------------
template <typename value_type>
class DerivedFoo : public Base < value_type, DerivedFoo<value_type> >
{
public:
    // Identify myself in the CRTP hierarchy.
    typedef DerivedFoo object_type;

protected:
    value_type m_Foo;

public:
    DerivedFoo(value_type base = value_type(), value_type foo = value_type()) :
        Base < value_type, DerivedFoo<value_type> >(base), m_Foo(foo) {}

    template <typename object_type_2>
    DerivedFoo(object_type_2 const &other)
    {
        static_assert(false, "Non-specialized template constructor disabled.");
    }

public:
    // A possible implementation of the delegated function
    void Dump()
    {
        std::cout << "DerivedFoo = [" << m_Base << ", " << m_Foo << "]\n";
    }
};

// ----------------------------------------------------------------------------
// Base -+---> DerivedFoo -+ Two-way
//       +---> DerivedBar -+ conversion
// ----------------------------------------------------------------------------
template <typename value_type, typename bar_type>
class DerivedBar : public Base < value_type, DerivedBar<value_type, bar_type> >
{
public:
    // Pass along the derived bar type.
    typedef bar_type bar_type;
    // Identify myself in the CRTP hierarchy.
    typedef DerivedBar object_type;
    // Delegate to derived types.
    bar_type & asLeaf() { return static_cast<bar_type &>(*this); }

protected:
    value_type m_Bar;

public:
    DerivedBar(value_type base = value_type(), value_type bar = value_type()) :
        Base < value_type, DerivedBar<value_type, bar_type> >(base), m_Bar(bar)
    {}

public:
    void Dump() { asLeaf().Dump(); }
};

// ----------------------------------------------------------------------------
// Base -+---> DerivedFoo <-------+
//       +-+-> DerivedBar <-------+ Three-way conversion
//         +---> DerivedBarCode <-+
// ----------------------------------------------------------------------------
template <typename value_type>
class DerivedBarCode :
    public DerivedBar < value_type, DerivedBarCode<value_type> >
{
public:
    typedef DerivedBarCode object_type;

protected:
    value_type m_Code = 8;

public:
    DerivedBarCode(value_type base = value_type(),
        value_type bar = value_type(), value_type code = value_type()) :
        DerivedBar < value_type, DerivedBarCode<value_type> >(base, bar),
        m_Code(code) {}

public:
    void Dump()
    {
        std::cout << "DerivedBarCode = ["
            << m_Base << ", " << m_Bar << ", " << m_Code << "]\n";
    }
};

// DerivedBarCode => DerivedFoo
// Example of what I'm trying to do:
template <typename value_type>
template <>
DerivedFoo<value_type>::DerivedFoo(
    typename DerivedBarCode<value_type>::object_type const &other)
{
    m_Base = other.m_Base;
    m_Foo = other.m_Foo;
    // There may be other calculations, e.g. replacing the line above with:
    // m_Foo = other.m_Foo + other.m_Code;
    std::cout << "m_Code = " << other.m_Code << '\n';
}

// ----------------------------------------------------------------------------
// Base -+---> DerivedFoo <-------+
//       +-+-> DerivedBar <-------+ Four-way
//         +---> DerivedBarCode <-+ conversion
//         +---> DerivedBarDoge <-+
// ----------------------------------------------------------------------------
template <typename value_type>
class DerivedBarDoge :
    public DerivedBar < DerivedBarDoge<value_type>, value_type > 
{
public:
    typedef DerivedBarDoge object_type;

protected:
    value_type m_Doge;

public:
    DerivedBarDoge(value_type base = value_type(),
        value_type bar = value_type(), value_type dance = value_type()) :
        DerivedBar < DerivedBarDoge<value_type>, value_type >(base, bar),
        m_Code(dance) {}

    void Dump()
    {
        std::cout << "DerivedBarDoge = ["
            << m_Base << ", " << m_Bar << ", " << m_Doge << "]\n";
    }

};

// DerivedBarDoge => DerivedFoo
// Another attempt.
template <typename value_type>
template <>
DerivedFoo<value_type>::DerivedFoo(DerivedBarDoge<value_type> const &other)
{
    m_Base = other.m_Base;
    m_Foo = other.m_Foo;
    std::cout << "m_Doge = " << other.m_Doge << '\n';
}


int main()
{
    DerivedFoo<double> foo(1.0, 2.0);
    foo.Dump();
    // Output:
    // DerivedFoo = [1, 2];

    DerivedBarCode<double> barcode(4.0, 8.0, 16.0);
    barcode.Dump();
    // Output:
    // DerivedBarCode = [4, 8, 16];

    DerivedBarDoge<double> bardoge(32.0, 64.0, 128.0);
    bardoge.Dump();
    // Output:
    // DerivedBarDoge = [32, 64, 128];

    DerivedFoo<double> converted(barcode);
    converted.Dump();
    // Expected output:
    // DerivedFoo = [4, 8];

    converted = bardoge;
    converted.Dump();
    // Expected output:
    // DerivedFoo = [32, 64];

    barcode = foo;
    barcode.Dump();
    // Expected output:
    // DerivedFoo = [1, 2, 16];

    // system("pause");
    return 0;
}

CRTP基础会将其类名注入到派生类中。 因此,如果每个CRTP基地台都知道派生类型为derived_type ,那么到达另一个基地other_base< derived_type >是一个简单的问题

static_cast< typename derived_type::other_base & >(
            static_cast< derived_type & >( * this ) );

如果CRTP基本mixin可以由多个模板中的任何一个实现,则应在其自己的类型中添加typedef,以标识其是哪个mixin。 然后将其用于::other_base

要添加转化,只需将这样的转换放入转化运算符模板中即可。 在实际尝试转换之前,请确保使用SFINAE清除转换。

template< typename t,
    std::enable_if_t< std::is_base_of_v< t, derived_type > > * = nullptr >
operator t & ()
    { return etc; }

您可以另外/替换地用成员typedef标记所有合适的mixin,然后SFINAE检查该标记。 要仅选择图中的过渡,请将标记升级为布尔元功能。

首先,停止考虑模板层次结构。 其次,不要再考虑铸造了。

似乎没有留下多少,但留下了足够的空间。

假设我们有3种无关的类型:爱丽丝,鲍勃和密斯。

任何东西都可以轻松转换为密集:

template<typename T>
Dense to_dense( T&& );

现在,有了自定义代码,可以将Dense转换为Alice或Bob。 让我们使用重载,在这里我们传递标签参数来告诉我们要转换为的内容:

template<class T> struct type_tag {};
Alice from_dense( Dense const&, type_tag<Alice> );
Bob from_dense( Dense const&, type_tag<Bob> );

这些将由AliceBob分别维护。

让我们假设AliceBob属于某种类型类别-称为ConvertUniverse

template<class T>
constexpr bool InConvertUniverse();

我们可以说InConvertUniverse<Alice>()并得到true ,而InConvertUniverse<int>()在编译时得到false 就目前而言, Dense不在所说的宇宙中。

让我们创建一个可以自动将所述Universe中的任何内容转换为其他任何内容的函数:

template<class T> using decay_t = typename std::decay<T>::type;

template<class Src, class Dest>
typename std::enable_if<
  InConvertUniverse< decay_t<Src> >() && InConvertUniverse< Dest >(),
  Dest
>::type
cross_convert( Src&& src, type_tag<Dest> ) {
  return from_dense( to_dense(src), type_tag<Dest>{} );
}

现在, cross_convert( alice, type_tag<Bob>{} )将返回Bob

我们可以使用它来实现对Alice任何InConvertUniverse<?>() template转换。 实际上,我们可以使用它在Alice的CRTP父级中实现此类强制转换。

对于从Dense铸造来说,你们都很好。

下一步是在特殊情况下允许更快的cross_convert 如果到此为止,您将需要使上面的全局cross_convert具有一些属性,使其不像ADL发现的重载那样cross_convert (例如,以从不使用...Ts&&...结束参数列表) )。 然后,我相信特定的cross_convert重载将是首选。

现在,上述内容的一个令人讨厌的缺点是,由于type_tag<Dest>{}存在于不相关的命名空间中(理论上),因此ADL不允许我们将from_denseBob的命名空间中并自动找到它。 因此,将type_tag替换为:

template<class T> using type_tag = T*;

然后ADL开始from_dense ,我们可以在Alice的命名空间中定义from_dense ,并通过cross_convert神奇地找到它。 它还会导致协变量重载,如果您有Alice子类在周围徘徊,这将很烦人,但这在这里不是问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM