部分模板专业化的g ++ Bug

时间:2013-07-17 12:32:35

标签: c++ templates c++11 template-specialization template-aliases

我正在为g ++(版本4.8.1_1,Macports)和clang ++(版本3.3,Macports)编写一些TMP密码。虽然g ++使用 UNBRIDLED FURY 拒绝以下代码清单,但clang ++使用 grace splendor 编译它。

  • 哪个编译器在右边? (我强烈怀疑它是g ++,但我希望在提交错误报告之前得到其他人的一些保证。)
  • 您有任何简单或优雅的变通方法可供建议吗? (我需要使用模板别名,因此切换到结构,导致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 ++的输出:

感谢您的帮助!

3 个答案:

答案 0 :(得分:8)

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

正如您所说,将extents从模板别名更改为类模板,可以解决问题。

答案 1 :(得分:3)

如果我理解正确判断以下模板特化之一是否比另一个更专业,那么问题就归结为:

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都不能与第二个专业化相匹配,因此第二个是第一个专门化

答案 2 :(得分:0)

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

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

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

C ++标准第14.8.2.5节第4段说:

  

在大多数情况下,用于组成P的类型,模板和非类型值参与模板参数推断。也就是说,它们可用于确定模板参数的值,并且如此确定的值必须与其他地方确定的值一致。但是,在某些上下文中,该值不参与类型推导,而是使用模板参数的值   在其他地方推断或明确指定。如果模板参数仅在非推导的上下文中使用且未明确指定,则模板参数推断将失败。

     

未推断的上下文是:

     

x使用qualified-id

指定的类型的嵌套名称说明符      

...

第14.8.2.4节第11段说:

  

在大多数情况下,所有模板参数都必须具有值以便推断成功,但是对于部分排序目的,模板参数可以保持不带值,前提是它不用于用于部分排序的类型。 [注意:使用非推断上下文中使用的模板参数。 - 后注]

所以我们处于非推导的上下文中,这意味着所有模板参数都必须具有值。因此,如果范围&lt;&gt;没有确定类型,那么根据标准,结果将是模糊的。合理?