我正在Scala中查看parboiled2,它有一种使用协变/逆变子类型在类型系统中编码解析器行为的有趣方式:
https://github.com/sirthias/parboiled2#rule-types-and-the-value-stack
我很好奇是否可以在C ++中使用模板元编程实现类似的东西。
我怀疑协变行为可以通过继承来模拟,但是逆变呢?
答案 0 :(得分:2)
在C ++中,与其他OO语言一样,子类型通常由继承表示。
但是,我们不能通过继承来模拟协方差和逆变关系,因为没有办法列出类的基类(没有任何反映提议,但尚未进入语言)。 / p>
允许这种行为的最简单方法是允许协变和逆变模板类根据相关类型的关系进行转换。
如果
Derived
是Base
,那么Covariant<Derived>
“应该是”Covariant<Base>
。
通常,解决方案是让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 */
}
};
如果
Derived
是Base
,那么Contravariant<Base>
“应该是”Contravariant<Derived>
这里的诀窍大致相同 - 允许将任何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链接,表明它可以正常工作