c++ - 使用 C++ 模板指定协变/逆变行为

标签 c++ scala types

我正在研究 Scala 中的 parboiled2,它有一种有趣的方式来使用协变/逆变子类型在类型系统中编码解析器行为:

https://github.com/sirthias/parboiled2#rule-types-and-the-value-stack

我很好奇是否可以使用模板元编程在 C++ 中完成类似的事情。

我怀疑协变行为可以通过继承来模拟,但是逆变呢?

最佳答案

在 C++ 中,与在其他 OO 语言中一样,子类型通常由继承表示。

但是,我们无法通过继承对协变和逆变关系进行建模,因为无法列出类的基类(没有任何各种反射提议,这些提议尚未进入语言)。

允许这种行为的最简单方法是允许协变和逆变模板类根据相关类型的关系进行转换。

协方差

If Derived is-a Base, then Covariant<Derived> "should-be-a" 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 */
    }
};

逆变

If Derived is-a Base, then Contravariant<Base> "should-be-a" 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显示它有效的链接

关于c++ - 使用 C++ 模板指定协变/逆变行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50427648/

相关文章:

C++ 无法使用 g++-mp-4.4 捕获从 Mac OS 中的 curlpp 抛出的异常

scala - 为什么这个简单的隐式 stringToInt 函数会导致堆栈溢出?

c++ - 如何在 C++ 中检查字符串是否具有有效的 UTF-8 字符?

c++ - 我的 C++ 程序无法打开命名管道

c++ - 在 C++ 应用程序中使用 id3lib 库时出现 undefined reference 链接器错误

matlab调用scala函数

scala - 类型参数子句中的广义约束?

c - "type domain"和 "real type"是什么意思?

c# - 如何通过重载使用泛型?

mysql - Doctrine 1.2 模型使用 Zend Framework 和 MySQL 5 将整数字段返回为字符串值