Question

I am writing some TMP-heavy code for g++ (version 4.8.1_1, Macports) and clang++ (version 3.3, Macports). While g++ rejects the following code listing with UNBRIDLED FURY, clang++ compiles it with grace and splendor.

  • Which complier is in the right? (I strongly suspect it's g++, but I want to get some reassurance from others before submitting a bug report.)
  • Do you have any easy or elegant workarounds to suggest? (I need to use template aliases, so switching over to structs, which causes g++ to accept the code, is not an option.)

Here is the code listing, made just for you.

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;
}

Here is g++'s output:

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;
                                           ^

Here is clang++'s output:

Thanks for your help!

Was it helpful?

Solution

This seems like a g++ bug because clearly foo<B, B, extents> is more specialized than foo<A, B, extents> (te latter can match anything that the former matches, but not vice versa), so the compiler should choose that specialization.

As you noted yourself, changing extents from a template alias to a class template, solves the problem.

OTHER TIPS

The question boils down if I understood it correctly to determining whether one of the following template specializations is more specialized than the other:

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;
};

And the answer is yes, for any combination of parameters that are allowed in the second specialization the same combination is allowed in the first by making the template arguments A == B. On the other direction, any instantiation of the first template specialization on which A != B cannot be a match for the second specialization, so the second is strictly more specialized than the first.

I believe g++ might be correct. The line

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

is using the template parameters in a non-deduced context, so it can't use the actual values given for the template parameters to resolve the ambiguity, only their types and that isn't sufficient.

Section 14.8.2.5, para 4 of the C++ standard says:

In most cases, the types, templates, and non-type values that are used to compose P participate in template argument deduction. That is, they may be used to determine the value of a template argument, and the value so determined must be consistent with the values determined elsewhere. In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

The non-deduced contexts are:

x The nested-name-specifier of a type that was specified using a qualified-id

...

Section 14.8.2.4 para 11 says:

In most cases, all template parameters must have values in order for deduction to succeed, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered used. —end note ]

So we're in a non-deduced context, which means that all template arguments must have values. So if extents<> fails to nail down the type, then the result will be ambiguous according to the standard. Plausible?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top