14

我正在为 g++(版本 4.8.1_1,Macports)和 clang++(版本 3.3,Macports)编写一些 TMP 繁重的代码。虽然 g++ 使用UNBRIDLED FURY拒绝以下代码列表,但 clang++ 以优雅辉煌的方式编译它。

  • 哪个编译器是正确的?(我强烈怀疑它是 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++ 的输出:

谢谢你的帮助!

4

3 回答 3

8

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

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

于 2013-07-17T12:49:15.910 回答
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都不能与第二个特化匹配,因此第二个比第一个更特化。

于 2013-07-17T12:59:41.703 回答
0

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

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

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

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

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

未推断的上下文是:

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

...

第 14.8.2.4 节第 11 段说:

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

所以我们处于非推导上下文中,这意味着所有模板参数都必须具有值。因此,如果 extents<> 无法确定类型,则根据标准,结果将是模棱两可的。合理吗?

于 2013-07-17T19:32:57.450 回答