Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pack expansion failure with g++ 4.9.0, but works with clang++ 3.4

For the code below:

template <typename... Ts>
struct Set {};

template <typename T, typename... Ts>
using Tail = Set<Ts...>;

template <typename T, typename TS>
struct MemberOf;

template <typename T, typename... Ts>
struct MemberOf<T, Set<T, Ts...>> {
    static constexpr bool value = true;
};

template <typename T>
struct MemberOf<T, Set<>> {
    static constexpr bool value = false;
};

template <typename T, typename... Ts>
struct MemberOf<T, Set<Ts...>> {
    static constexpr bool value = false || MemberOf<T, Tail<Ts...>>::value;
};

g++ 4.9.0 gives:

ts.cpp:27:63: error: pack expansion argument for non-pack parameter 'T' of alias template 'template<class T, class ... Ts> using Tail = Set<Ts ...>'
     static constexpr bool value = false || MemberOf<T, Tail<Ts...>>::value;
                                                               ^
ts.cpp:4:11: note: declared here
 template <typename T, typename... Ts>
           ^
ts.cpp:27:66: error: template argument 2 is invalid
     static constexpr bool value = false || MemberOf<T, Tail<Ts...>>::value;
                                                                  ^

clang++ 3.4 compiles it without any diagnostic. It looks like a g++ bug to me, but just wanted to confirm.

ADDENDUM: So, based on the great answers below, the problem seems to be that a pack argument must correspond exactly to a pack parameter in an alias template. In other words:

template <typename T, typename... Ts>
using Tail = Set<Ts...>;

template <typename... Ts>
using Alias1 = Tail<Ts...>; // ERROR, Ts doesn't correspond directly to the pack in Tail.

template <typename... Ts>
using Alias2 = Tail<int, Ts...>; // Okay, now it does.
like image 262
kec Avatar asked May 01 '14 20:05

kec


1 Answers

Newer versions of clang now reject your code as well, due to the fix for the following bug:

http://llvm.org/bugs/show_bug.cgi?id=18401

Richard Smith's comment on the fix is:

Fix assert by implementing the current proposed direction of core issue 1430. Don't allow a pack expansion to be used as an argument to an alias template unless the corresponding parameter is a parameter pack.

The referenced core defect includes, as an example, code which is essentially identical to yours:

template<class... x> class list{};
template<class a, class... b> using tail=list<b...>;
template <class...T> void f(tail<T...>);

int main() {
  f<int,int>({});
}

And the defect notes:

There is implementation variance in the handling of this example.

The earlier example given by the defect is a more difficult case that seems to require templates aliases to not behave as transparent aliases for templates. Because of this implementation issue the committee is apparently leaning toward prohibiting using template aliases in certain ways, and compilers implementers seem to be going with that solution.

So my understanding is that this code is valid under the current spec as written, but there are implementation problems and the committee may eventually prohibit it.

like image 184
bames53 Avatar answered Nov 09 '22 14:11

bames53