如何解决这个歧义?

How to resolve this ambiguity?

在下面的代码中:

template <int...> struct IndexSequence {};

template <int, int, typename, int...> struct Helper;

template <int Start, typename Coefficients, int... Is>
struct Helper<Start, Start, Coefficients, Is...> {
    using type = IndexSequence<Is...>;
};

template <int Start, int N, int... As, int... Is>
struct Helper<Start, N, IndexSequence<As...>, Is...> :
    Helper<Start, N-1, IndexSequence<As...>, N-1, Is...> {};

int main() {
    Helper<2,5, IndexSequence<1,2,3>>::type a;
}

我得到编译错误:

ambiguous class template instantiation for 'struct Helper<2, 2, IndexSequence<1, 2, 3>, 2, 3, 4>'

我认为它会解决专业化问题

template <int Start, typename Coefficients, int... Is>
struct Helper<Start, Start, Coefficients, Is...> {
    using type = IndexSequence<Is...>;
};

但我猜它也在读

struct Helper<Start, N, IndexSequence<As...>, Is...> :
    Helper<Start, N-1, IndexSequence<As...>, N-1, Is...> {};

那么如何解决这个歧义呢?

问题本质上是 none 个专业比另一个更专业。

在决定选择哪个偏特化时,首先将特化与参数进行匹配,以检查它们是否可行(这里,两者都是)。如果多个是可行的,那么为了决定选择哪一个,我们必须检查哪一个比所有其他的更专业。这个过程被称为 "partial ordering" 并通过比较专门的模板 "arguments" 来完成(即 Helper<...> 中的模板参数) - 一个模板被视为参数模板,另一个是参数模板。参数模板提供类型,并执行推导以查看是否可以根据参数模板的类型推导参数模板中的模板参数。

template <int Start, typename Coefficients, int... Is>
struct Helper<Start, Start, Coefficients, Is...>
//            ^^^^^  ^^^^^  ^^^^^^^^^^^^  ^^^^^
//   A:         1      2         3          4

template <int Start, int N, int... As, int... Is>
struct Helper<Start, N, IndexSequence<As...>, Is...>
//            ^^^^^  ^  ^^^^^^^^^^^^^^^^^^^^  ^^^^^
//   B:         1    2            3             4

对于参数模板的每个参数,为每个模板参数组成一些唯一值并代入参数。然后将这些转换后的参数传递给参数模板,以检查推导是否成功。例如:

  • A1 推导 B1,成功。 (int可以从int推导出来)
  • B1 是针对 A1 推导出来的,... 如上所述。工作正常。

现在让我们看看关键的推论:

  • B2 是针对 A2 推导出来的,但是由于我们对 StartN 使用了唯一值,模板 A 中的 Start 是推导不一致,也就是说这里推导失败

  • A3 是针对 B3 推导的,但是由于 Coefficients 是某种独特的类型(不是 IndexSequence 的特化!),推导再次失败。

演绎在两个方向上至少失败一次:因此没有一个模板在整体上比另一个更专业。


通过例如解决此部分排序歧义将第一个专业写成

template <int Start, int... As, int... Is>
struct Helper<Start, Start, IndexSequence<As...>, Is...> {
    using type = IndexSequence<Is...>;
};

现在,上面失败的第二次推导不再失败了。只有一个以专业 B 为参数模板。这意味着 B 更专业,因此被选中。

Demo.