g++错误与部分模板专业化

g++ Bug with Partial Template Specialization

本文关键字:专业化 错误 g++      更新时间:2023-10-16

我正在为g++(4.8.1_1版本,Macports)和clang++(3.3版本,Macport)编写一些TMP繁重的代码。当g++用UNBRIDLED FURY拒绝下面的代码列表时,clang++用grace精彩编译它。

  • 哪个同谋是对的?(我强烈怀疑这是g++,但在提交错误报告之前,我想从其他人那里得到一些保证。)
  • 你有什么简单或优雅的解决方案可以建议吗?(我需要使用模板别名,所以切换到structs(这会导致g++接受代码)不是一个选项。)

这是代码列表,仅为您制作

template <class... Ts>
struct sequence;
template <int T>
struct integer;
// This definition of `extents` causes g++ to issue a compile-time error.
template <int... Ts>
using extents = sequence<integer<Ts>...>;
// However, this definition works without any problems.
// template <int... Ts>
// struct extents;
template <int A, int B, class Current>
struct foo;
template <int A, int B, int... Ts>
struct foo<A, B, extents<Ts...>>
{
    using type = int;
};
template <int B, int... Ts>
struct foo<B, B, extents<Ts...>>
{
    using type = int;
};
int main()
{
    using t = foo<1, 1, extents<>>::type;
    return 0;
}

这是g++的输出:

er.cpp: In function 'int main()':
er.cpp:39:41: error: ambiguous class template instantiation for 'struct foo<1, 1, sequence<> >'
  using t = typename foo<1, 1, extents<>>::type;
                                         ^
er.cpp:26:8: error: candidates are: struct foo<A, B, sequence<integer<Ts>...> >
 struct foo<A, B, extents<Ts...>>
        ^
er.cpp:32:8: error:                 struct foo<B, B, sequence<integer<Ts>...> >
 struct foo<B, B, extents<Ts...>>
        ^
er.cpp:39:43: error: 'type' in 'struct foo<1, 1, sequence<> >' does not name a type
  using t = typename foo<1, 1, extents<>>::type;
                                           ^

这是clang++的输出:

谢谢你的帮助!

这看起来像是一个g++错误,因为显然foo<B, B, extents>foo<A, B, extents>更专业(后者可以匹配前者匹配的任何内容,但不能反之亦然),所以编译器应该选择该专业化。

正如您自己所指出的,将extents从模板别名更改为类模板解决了这个问题。

如果我正确理解,问题可以归结为确定以下模板专业化中的一个是否比另一个更专业:

template <int A, int B, class Current>
struct foo;
template <int A, int B, int... Ts>
struct foo<A, B, extents<Ts...>>
{
    using type = int;
};
template <int B, int... Ts>
struct foo<B, B, extents<Ts...>>
{
    using type = int;
};

答案是,对于第二个专业化中允许的任何参数组合,通过使模板参数A == B,第一个专业化允许相同的组合。另一方面,A != B不能与第二个专门化匹配的第一个模板专门化的任何实例化,因此第二个严格地比第一个更专门化

我相信g++可能是正确的。线路

using t = foo<1, 1, extents<>>::type

是在非推导上下文中使用模板参数,因此它不能使用为模板参数给定的实际值来解决歧义,只能使用它们的类型,这是不够的。

C++标准第14.8.2.5节第4段规定:

在大多数情况下,用于组成p的类型、模板和非类型值参与模板参数推导。也就是说,它们可以用于确定模板参数的值,并且这样确定的值必须与其他地方确定的值一致。然而,在某些上下文中,该值不参与类型推导,而是使用模板参数的值要么是在其他地方推导出来的,要么是明确规定的。如果模板参数仅在非推导上下文中使用,并且没有明确指定,则模板参数推导失败。

未推导的上下文为:

x使用限定id 指定的类型的嵌套名称说明符

第14.8.2.4节第11段规定:

在大多数情况下,所有模板参数都必须有值才能成功推导,但出于部分排序的目的,模板参数可以保留为没有值,前提是它不用于用于部分排序的类型。[注:在非推导上下文中使用的模板参数被视为已使用。--尾注]

因此,我们处于非推导上下文中,这意味着所有模板参数都必须有值。因此,如果区段<>如果没有确定类型,那么根据标准,结果将是模糊的。看似合理的