Consider the C++ 17 code below, which tests a set of enum values to see if another enum value is contained in that set:
enum Flag { val1, val2, val3, val4, val5 };
template<Flag arg> struct Value {
template<Flag... set> struct IsIn {
static constexpr bool value =
static_cast<bool>(((set == arg) || ...));
};
};
This works as intended:
bool x = Value<val4>::IsIn<val1, val2, val5>::value;
// x == false
bool y = Value<val2>::IsIn<val3, val2>::value;
// y == true
However, I wish to test if all of a set of values are contained within another set, like so:
template<Flag... args> struct Values {
template<Flag... set> struct AreIn {
static constexpr bool value =
static_cast<bool>((Value<args>::IsIn<set...>::value && ...));
};
};
The above does not compile on GCC 7.3 or Clang 5.0; they both give rather cryptic answers that give little insight into the problem. Given that parameter pack expansion in a template parameter list is allowed (as long as the template supports the expansion), I'm having a hard time figuring out why this isn't legal C++.
You've run into one of the most irritating problems in the C++ syntax: non-inferrable dependent names.
When you do Foo<Bar>::Baz<Quux>
, since Foo<Bar>
is a dependent name, you must put the template
keyword before Baz
in order to prevent the parser from running off a cliff. Clang is usually quite good at explicitly telling you about this with a helpful error, but in some cases (like yours) it just explodes and says Expected )
or something equally unhelpful.
For more information, see this other question
So, all you have to do to fix your template, is add the template
keyword on the dependent template invocation:
template<Flag... args>
struct Values {
template<Flag... set>
struct AreIn {
static constexpr bool value =
static_cast<bool>((Value<args>::template IsIn<set...>::value && ...));
};
};
Note, also, that the static_cast<bool>()
is redundant.
This solves your problem: replace
static_cast<bool>((Value<args>::IsIn<set...>::value && ... ))
by
static_cast<bool>((Value<args>::template IsIn<set...>::value && ... ))
Why is this? At the point of parsing the declaration of AreIn
, the compiler can't know what possible types Value<args>
might resolve to. Perhaps, at a later point in the file, you'd be going to declare a specialization of Values
that does not contain a template subclass IsIn
but rather a field of that name. Using the template
keyword promises to the compiler that IsIn
is expected to be some sort of template, so the following <
... >
get parsed as template arguments, rather than comparison operators with set...
and with ::value
(a variable in the global namespace), which would also make sense.
Of course, one could ask why the compiler does not wait till it knows that you are using Values<val1, val2>
, for which there indeed is a templated subclass AreIn<val1, val2, val5>
which has a static member ::value
. But parsing ahead and remembering a partially processed syntax tree is just how today's compilers work, and the standard makes the template
hint mandatory for the above reasons.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With