Вопрос

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.
Это было полезно?

Решение

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.

Другие советы

The use of a template alias is supposed to be such that the alias can be expanded where it is used.

After your definition of

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

you use

Tail<Ts...>

but this cannot be replaced by anything involving Set<...>.

(I must admit I'm having trouble finding a reference to support this interpretation, so I don't know for sure if this is what the standard states, or merely what compilers implement.)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top