[英]Conversion between sub-classes using Curiously Recursive Template Pattern
value_type
的模板类Base
, DerivedFoo
和DerivedBar
,以及 DerivedBarCode
和DerivedBarDoge
这是源自DerivedBar
, 该图:
Base -+---> DerivedFoo <-------+
+-+-> DerivedBar | // Three-way
+---> DerivedBarCode <-+ // conversion
+---> DerivedBarDoge <-+
MatrixBase -+---> DenseMatrix <------------+
+-+-> SparseMatrixBase |
+---> MatrixCSR <------------+ // Multiple-way
+---> MatrixCSC <------------+ // conversion
+---> MatrixModifiedCSR <----+
+---> MatrixModifiedCSC <----+
其中每个类至少具有一个公共模板参数typename value_type
。
说明:
DerivedBarCode<value_type>
和DerivedBarDoge<value_type>
转换为DerivedFoo<value_type>
。 码:
#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> );
这些将由Alice
和Bob
分别维护。
让我们假设Alice
和Bob
属于某种类型类别-称为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_dense
在Bob
的命名空间中并自动找到它。 因此,将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.