I just came across the mysterious typename.... What is its semantics?
Obviously it's too general to mean something very specific but still
namespace detail
{
template <typename...>
using Void = void;
template <typename, typename = void>
struct EqualityComparableToNullptr
: std::false_type {};
template <typename T>
struct EqualityComparableToNullptr<T, Void<decltype (std::declval<T>() != nullptr)>>
: std::true_type {};
} // namespace detail
It is an unnammed variadic template parameter. It is used to help with SFINAE in partially specialized classes. This technique was even added to the standard as std::void_t.
If you had
template <typename>
using Void = void;
You would only be able to "convert" one type to void, whereas having a variadic template allows you to have N types "convert" to a single void type.
Related
I needed a type trait that decays enums to their underlying type, and works the same as decay_t for all other types. I've written the following code, and apparently this is not how SFINAE works. But it is how I thought it should work, so what exactly is the problem with this code and what's the gap in my understanding of C++?
namespace detail {
template <typename T, std::enable_if_t<!std::is_enum_v<T>>* = nullptr>
struct BaseType {
using Type = std::decay_t<T>;
};
template <typename T, std::enable_if_t<std::is_enum_v<T>>* = nullptr>
struct BaseType {
using Type = std::underlying_type_t<T>;
};
}
template <class T>
using base_type_t = typename detail::BaseType<T>::Type;
The error in MSVC is completely unintelligible:
'detail::BaseType': template parameter '__formal' is incompatible with
the declaration
In GCC it's a bit better - says that declarations of the second template parameter are incompatible between the two BaseType templates. But according to my understanding of SFINAE, only one should be visible at all for any given T and the other one should be malformed thanks to enable_if.
Godbolt link
SFINAE applied to class templates is primarily about choosing between partial specialisations of the template. The problem in your snippet is that there are no partial specialisations in sight. You define two primary class templates, with the same template name. That's a redefinition error.
To make it work, we should restructure the relationship between the trait implementations in such as way that they specialise the same template.
namespace detail {
template <typename T, typename = void> // Non specialised case
struct BaseType {
using Type = std::decay_t<T>;
};
template <typename T>
struct BaseType<T, std::enable_if_t<std::is_enum_v<T>>> {
using Type = std::underlying_type_t<T>;
};
}
template <class T>
using base_type_t = typename detail::BaseType<T>::Type;
The specialisation provides a void type for the second argument (just like the primary would be instantiated with). But because it does so in a "special way", partial ordering considers it more specialised. When substitution fails (we don't pass an enum), the primary becomes the fallback.
You can provide as many such specialisation as you want, so long as the second template argument is always void, and all specialisations have mutually exclusive conditions.
BaseType isn't being partial-specialized, you're just redeclaring it, and since the non-type parameter has a different type, the compilation fails. you might want to do
#include <type_traits>
namespace detail {
template <typename T, bool = std::is_enum_v<T>>
struct BaseType;
template <typename T>
struct BaseType<T, false> {
using Type = std::decay_t<T>;
};
template <typename T>
struct BaseType<T, true> {
using Type = std::underlying_type_t<T>;
};
}
You declare the same struct with different parameter, which is forbidden.
You can do it with partial specialization:
namespace detail {
template <typename T, typename Enabler = void>
struct BaseType {
using Type = std::decay_t<T>;
};
template <typename E>
struct BaseType<E, std::enable_if_t<std::is_enum_v<E>>>
{
using Type = std::underlying_type_t<E>;
};
}
Demo
consider this example:
#include <iostream>
template <typename T, std::size_t I>
struct Foo
{
};
template <typename T>
struct specializes_foo : std::false_type {};
template <typename T, std::size_t I>
struct specializes_foo<Foo<T, I>> : std::true_type {};
template <typename T>
concept foo = specializes_foo<T>::value;
int main()
{
std::cout << foo<int> << "\n";
std::cout << foo<Foo<int,3>> << "\n";
}
is there a shorter way in C++20? I know that you could do a concept for "specializes template", e.g.:
// checks if a type T specializes a given template class template_class
// note: This only works with typename template parameters.
// e.g.: specializes<std::array<int, 3>, std::array> will yield a compilation error.
// workaround: add a specialization for std::array.
namespace specializes_impl
{
template <typename T, template <typename...> typename>
struct is_specialization_of : std::false_type {};
template <typename... Args, template <typename...> typename template_class>
struct is_specialization_of<template_class<Args...>, template_class> : std::true_type {};
}
template <typename T, template <typename...> typename template_class>
inline constexpr bool is_specialization_of_v = specializes_impl::is_specialization_of<T,template_class>::value;
template <typename T, template <typename...> typename template_class>
using is_specialization_of_t = specializes_impl::is_specialization_of<T,template_class>::type;
template<typename T, template <typename...> typename template_class>
concept specializes = is_specialization_of_v<T, template_class>;
However, this fails with non-type template parameters such as "size_t". Is there a way so I could, for example just write:
void test(Foo auto xy);
I know that since C++20 there came a couple of other ways to constrain template parameters of a function, however, is there a short way to say "I dont care how it specializes the struct as long as it does"?
Nope.
There's no way to write a generic is_specialization_of that works for both templates with all type parameters and stuff like std::array (i.e. your Foo), because there's no way to write a template that takes a parameter of any kind.
There's a proposal for a language feature that would allow that (P1985), but it's still just a proposal and it won't be in C++23. But it directly allows for a solution for this. Likewise, the reflection proposal (P1240) would allow for a way to do this (except that you'd have to spell the concept Specializes<^Foo> instead of Specializes<Foo>).
Until one of these things happens, you're either writing a bespoke concept for your template, or rewriting your template to only take type parameters.
I have many EnableIf traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve trait that can be used to transform those into a boolean trait.
Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
Now if you have an EnableIf trait like so
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
You can create a boolean version of that very quickly
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Or the analogous variable template.
But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>> to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}
I have traits classes sprinkled about my code which follow the same basic idiom:
template<class Frame, typename = void>
struct frame_traits
{
typedef void base_frame_type;
};
template<class Frame>
struct frame_traits<Frame, typename std::void_t<
typename Frame::base_frame_type>::type>
{
typedef typename Frame::base_frame_type base_frame_type;
};
and I have a bunch of trait checkers which use them, which also follow a similar idiom:
template <typename T>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename frame_traits<T>::base_frame_type, void>::value>::type {};
however, it turns out that has_base_frame_type has become useful to multiple concepts in my code, and I'd like to generalize it further so that I can pass the traits class as an additional parameter:
template <typename T, template<typename> class Traits = frame_traits>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename Traits<T>::base_frame_type, void>::value>::type {};
This doesn't work though, since templates with default arguments cannot be used as template template parameters.
I know I could work around the problem if I always use a traits class in the template instantiation (and modify the trait checker to accept it), namely
has_base_frame_type<frame_traits<MyClass>>::value
but I don't want to do that, because it would be all too easy to forget and pass in a non-trait class. In fact, that's how I originally had the code written until I forgot the trait one too many times and refactored it.
Is there someway I can modify my trait class idiom to work around the template template parameter problem?
Framework:
#include <type_traits>
template <typename...>
using void_t = void;
template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};
template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};
template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void, Operation, Args...>;
Detectors:
template <class Frame>
using frame_traits = typename Frame::base_frame_type;
template <class Frame>
using other_frame_traits = typename Frame::other_frame_type;
Trait with a default detector:
template <typename T, template <typename...> class Traits = frame_traits>
using has_frame_type = detect<Traits, T>;
Test:
struct A
{
using base_frame_type = void;
};
struct B
{
using other_frame_type = void;
};
int main()
{
static_assert(has_frame_type<A>{}, "!"); // default
static_assert(!has_frame_type<B>{}, "!"); // default
static_assert(!has_frame_type<A, other_frame_traits>{}, "!"); // non-default
static_assert(has_frame_type<B, other_frame_traits>{}, "!"); // non-default
}
DEMO
If I want some type to be specialised on its template parameter I generally use a struct:
template <bool value>
struct IsTrue;
template <>
struct IsTrue<true> : std::true_type {};
template <>
struct IsTrue<false> : std::false_type {};
An empty struct that gets its only functionality from inheritance isn't really that different from a using statement, so I was wondering, does something like template specialisation exist for using statements? Pseudocode of what I'm after below:
template <bool value>
using IsTrue;
template <>
using IsTrue<true> = std::true_type;
template <>
using IsTrue<false> = std::false_type;
Is something like this possible? What would it be called?
No, alias templates cannot be partially or explicitly specialized.
An earlier design did allow specialization, but the resulting semantics are rather...odd, at least viewed from today's angle. For example, in such a design, the following program would declare two different function templates:
template<class, class> class Meow {};
template<class T> using MeowInt = Meow<int, T>;
template<class> void f(Meow<int, T>);
template<class> void f(MeowInt<T>);
and this call would not compile because you wouldn't be able to deduce the template argument:
template<class T> using Purr = T;
template<class T> void f(Purr<T>);
f(42);