我正在Scala中查看parboiled2,它有一种有趣的方式来使用协变/相反变量子类型在类型系统中编码解析器行为:
https://github.com/sirthias/parboiled2#rule-types-and-the-value-stack
我很好奇,是否可以使用模板元编程在C++中完成类似的工作。
我怀疑协变量的行为可以用继承来模拟,但是协变量呢?
最佳答案
与其他OO语言一样,在C++中,子类型通常由继承表示。
但是,由于无法列出类的基类(没有各种反射建议中的任何一种,但尚未纳入语言中),因此无法通过继承对协方差和矛盾关系建模。
允许这种行为的最简单方法是允许协变量和反变量模板类根据相关类型的关系进行转换。
协方差
通常,解决方案是使Covariant<Derived>
从Covariant<Base>
继承,但是目前我们仅通过Base
就无法找到Derived
。但是,我们可以通过使用任何Covariant<Base>
编写Covariant<Derived>
的构造函数来启用转换:
template <typename T>
struct Covariant {
template <typename Derived>
Covariant(const Covariant<Derived>& derived,
std::enable_if_t<std::is_base_of_v<T, Derived>>* = nullptr)
{
/* Do your conversion here */
}
};
逆差
这里的技巧很相似-允许将任何
Contravariant<Base>
转换为Contravariant<Derived>
:template <typename T>
struct Contravariant {
template <typename Base>
Contravariant(const Contravariant<Base>& base,
std::enable_if_t<std::is_base_of_v<Base, T>>* = nullptr)
{
/* Do your conversion here */
}
};
然而
这有一个主要缺点:您需要手动执行转换,并且要注意偶然的object slicing可能会破坏您转换回来的能力(例如,如果您定义了协变容器类型,则将引起严重的麻烦)。
从本质上讲,除非反射使我们能够自动执行这种继承关系,否则转换是实现此关系的唯一方法,并且我不建议将其用于任何复杂的情况。一旦将
T
的对象存储在协变/相反变量类中,您将遭受巨大的伤害。这是一个Godbolt链接,表明它有效