Reputation: 1
I am new to template metaprogramming and I was watching the type traits talk part II by Jody Hagins. I wanted to replicate a function overload resolution example to detect whether a given type is constant using the following:
namespace detail {
template <typename T>
std::true_type is_const(T const);
template <typename T>
std::false_type is_const(T);
} // namespace detail
template <typename T>
using is_constant = decltype(detail::is_const(std::declval<T>()));
static_assert(is_constant<int const>::value);
The above static assertion produces a compiler error saying call to is_const
is ambiguous. If I use a TypeTag
to enclose T
in my template declarations, things work as expected:
template <typename T>
struct TypeTag {};
namespace detail {
template <typename T>
std::true_type is_const(TypeTag<T const>);
template <typename T>
std::false_type is_const(TypeTag<T>);
} // namespace detail
template <typename T>
using is_constant = decltype(detail::is_const(std::declval<TypeTag<T>>()));
static_assert(is_constant<int const>::value);
I am confused as to why the first declarations without TypeTag
encapsulation are ambiguous. My guess is it has something to do with declval
return type being T
for cv-qualified
types but then I do not understand how the second case works.
Is it because in the first case declval<int const>
has return type int but in the second case declval<TypeTag<int const>>
has return type TypeTag<int const>
so the compiler picks the first template specialization where T
is replaced with int and the template invocation looks like this:
<>
std::true_type is_const<TypeTag<int const>>;
If my guess is correct, is there a general practice to use tag dispatching with a TypeTag
(empty struct template) to guard against cv-qualified
types?
Upvotes: 0
Views: 247
Reputation: 7528
The reason the 1st example does not work is that top-level const
in a function argument is ignored, so is_const(T)
and is_const(T const)
are the same function signatures. If const
is not top-level, function signatures are different, e.g. is_const(T*)
and is_const(T* const)
are different.
In your 2nd example, is_const(TypeTag<T>)
and is_const(TypeTag<T const>)
are different because TypeTag<T>
and TypeTag<T const>
are unrelated types.
However, I don't think that your use of TypeTag
qualifies as "tag dispatch".
Upvotes: 1