【问题标题】:c++ function cannot derive template for child class of base class with templatesc++函数不能用模板为基类的子类派生模板
【发布时间】:2018-05-14 14:28:24
【问题描述】:

我对模板有些陌生,当我以设置模板的方式从基类继承时,我不明白编译器如何为子类派生模板。

我正在创建一个遗传算法基类,为此我为群体中的个体编写了一个抽象基类。我想要一个通用的定义,所以我使用模板来定义 fenotype 和 genotype:

template<typename T, typename S>
class individual {
    public:
        individual(S& fenotyp, T& genotyp) :
            fenotype(fenotyp), genotype(genotyp) {}
        ...
        S fenotype;

        T genotype;
        ...
};

当个体是位串时,我有以下子类:

class bitstring_individual : public individual<boost::dynamic_bitset<>,
    boost::dynamic_bitset<>> {
    public:
        using individual::individual;
        ...
};

现在我不再需要使用模板括号了。再往下,我有一个函数,给定人口std::vector&lt;individual&lt;T,S&gt;&gt;,返回适合度最高的一半。这适用于任何类型的个人,因此我们可以保持通用的定义:

template<typename T, typename S>
std::vector<individual<T,S>> select_best_half(std::vector<individual<T,S>> parents,
        std::vector<individual<T,S>> children) {
            ...
        }

但是,如果我调用此函数,我会得到error: no matching function for call to select_best_half(...),编译器会显示template argument deduction/substitution failed:mismatched types ‘individual&lt;T, S&gt;’ and ‘bitstring_individual'

bitstring_individual的定义中我们看到:

bitstring_individual : individual<boost::dynamic_bitset<>,boost::dynamic_bitset<>>

那么为什么编译器不理解模板应该是boost::dynamic_bitset&lt;&gt;?有人可以帮助我了解编译器如何处理这种继承以及如何修复它?

【问题讨论】:

标签: c++ templates inheritance


【解决方案1】:

(using bitset = boost::dynamic_bitset)

您的bitstring_individualindividual&lt;bitset, bitset&gt;相同,编译器理所当然地无法识别它们。一个继承另一个,是的,但这并不能使它们在任何地方都可以互换 - 特别是在用作模板参数时。

简而言之:不同(甚至多态相关)类型的向量(和其他容器)不是协变的。就像您不能将 std::vector&lt;int&gt; 传递给期望 std::vector&lt;long&gt; 的函数一样,您也不能将 std::vector&lt;bitstring_individual&gt; 传递给期望 std::vector&lt;individual&lt;bitset, bitset&gt;&gt; 的函数。

注意:是的,它们是不同的转换,但思路是一样的。

假设sizeof(individual&lt;bitset, bitset&gt;) = 32bitstring_individual 添加了一些成员,因此sizeof(bitstring_individual) = 48。如果编译器推断出T = S = bitset,那么它将生成一个包含std::vector&lt;individual&lt;bitset, bitset&gt;&gt; 的方法签名,因此是一个元素大小为32 的向量。但是当您尝试调用它时,您传递的是一个元素大小为48 的向量。这些向量不是协变的,这总是会导致问题。

如果您希望您的具体个体除了模板化基类提供的功能之外没有其他功能,只需执行以下操作:

using bitstring_individual = individual<bitset, bitset>;

否则,您的向量无法直接存储个体 - 您必须对所有人口向量使用 std::vector&lt;std::shared_ptr&lt;individual&lt;T, S&gt;&gt;&gt;(或者 unique_ptrref 而不是 shared_ptr)。

【讨论】:

  • 这很有道理,非常感谢。我认为我不需要额外的功能,所以我会尝试你的第一个建议。
【解决方案2】:

您的问题实际上涉及 C++ 中(某些)类型的 covariance and contravariance。即使您要“硬编码”您的模板参数,即有:

using i_bs_bs = individual<bitset, bitset>;
using std::vector;

class bitstring_individual : public i_bs_bs { ... };

vector<bsi> select_best_half(vector<i_bs_bs> parents, vector<i_bs_bs> children) {
    ...
}

vector&lt;bitstring_individual&gt; 传递给select_best_half() 时仍然会出错。为什么?因为,在 C++ 中 std::vector&lt;T&gt; 不是协变类型构造函数

以链接到 Wikipedia 页面中的示例为例,假设您的继承类是 Animal(基类)和 Cat(派生类)。在 C++ 中,您不能将 Animal 添加到 Cats 的向量中。该向量的所有元素都必须是 Cats。同样,正如@MaxLanghof 的回答所解释的,您不能将bitstring_individual 添加到其元素为bitstring_individual 的基本类型的向量中。处理bitstring_individual 所需的任何特殊行为都不会应用于vector&lt;i_bs_bs&gt; 的元素。

【讨论】:

  • 我将更彻底地阅读维基百科页面,但如果我理解正确的话,如果select_best_half 只是拿一个bitstring_individual 并返回它会起作用,因为我会绕过std::vector&lt;T&gt; .
  • 我真的很困惑,为什么你的代码不起作用?我知道,如果我要为 i_bs_bs 定义 select_best_half,它将无法正常工作。但是在您的代码中,您专门为vector&lt;bsi&gt; 定义了它,所以我希望它能够工作。
  • bsi 未声明。如果您打算将其别名为 bitstring_individual,则代码为 compiles without issue
  • @MaxLanghof:已修复。
  • @RichardSchoonhoven:已修复。
猜你喜欢
  • 2021-04-20
  • 1970-01-01
  • 1970-01-01
  • 2017-01-13
  • 1970-01-01
  • 1970-01-01
  • 2021-08-09
  • 2019-11-09
  • 1970-01-01
相关资源
最近更新 更多