Compiling this:
template < class T, class Y, class ...Args >
struct isSame
{
static constexpr bool value = std::conditional<
sizeof...( Args ),
typename std::conditional<
std::is_same< T, Y >::value,
isSame< Y, Args... >, // Error!
std::false_type >::type,
std::is_same< T, Y > >::type::value;
};
int main()
{
qDebug() << isSame< double, int >::value;
return EXIT_SUCCESS;
}
Gives me this compiler error:
error: wrong number of template arguments (1, should be 2 or more)
The issue is that isSame< double, int >
has an empty Args
parameter pack, so isSame< Y, Args... >
effectively becomes isSame< Y >
which does not match the signature.
But my question is: Why is that branch being evaluated at all? sizeof...( Args )
is false
, so the inner std:conditional
should not be evaluated. This isn't a runtime piece of code, the compiler knows that sizeof..( Args )
will never be true
with the given template types.
If you're curious, it's supposed to be a variadic version of std::is_same
, not that it works...
But my question is: Why is that branch being evaluated at all?
Because there is no evaluation, and thats not a branch at all. Thats a template and its instanced to successfully instance the std::conditional
template.
What I mean here is that the evaluation abstraction is usefull when writting template metaprograms, but you should never forget what the template system is really doing.
If you need to conditionally instantiate a potentially ill-formed template, add a level of indirection. Check this answer for an example of that.
You have an error because the type has to be correct when used as template parameter.
You may use template specialization to solve your issue, something like:
#include <type_traits>
template <typename ... Ts> struct are_same;
template <> struct are_same<> : std::true_type {};
template <typename T> struct are_same<T> : std::true_type {};
template <typename T1, typename T2, typename... Ts>
struct are_same<T1, T2, Ts...> :
std::conditional<
std::is_same<T1, T2>::value,
are_same<T2, Ts...>,
std::false_type
>::type
{};
static_assert(are_same<char, char, char>::value, "all type should be identical");
static_assert(!are_same<char, char, int>::value, "all type should not be identical");
std::conditional
In short it's because the template argument substitution happens outside of std::conditional
, which means that it's not std::conditional
that is causing the error.. it's isSame
.
Imagine std::conditional
being a function call (which it isn't), what we pass as parameters are evaulated before the actual body of the function is evaluated, and what we pass as parameters sure must be a valid construct, even if the function itself doesn't use them.
Further reading is available in the below linked answer:
std::conditional
Add some indirection so that you don't instantiate isSame
if sizeof... (Args) == 0
, you could have a traited used as isSame_if_not_empty<Args..>::type
which would yield isSame<Args...>
if Args
is not empty, and something else if it is indeed empty.
Fix so that isSame
can be used with an empty variadic pack, yielding true, this is the sensible approach. If the description of isSame
is "all types passed are of the same type", an empty pack sure has "all of its types" of the same type.
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