I have a C++17 piece of SFINAE code
#include <type_traits>
template<typename>
constexpr bool dependentFalse = false;
template<typename T>
struct IAmEmpty
{
template<typename U=T, std::enable_if_t<dependentFalse<U>>* =nullptr>
auto deleteMe()
{}
};
template struct IAmEmpty<int>;
https://godbolt.org/z/vWW3favWj
I am trying to rework that to C++20 and I wonder:
#include <type_traits>
template<typename T>
struct IAmEmpty
{
template<typename U=T>
requires false
void deleteMe() {}
void deleteMeToo() requires false {}
};
template struct IAmEmpty<int>;
As far as I understand it, leading requires is close to pre-C++20 SFINAE achieved using e.g. enable_if
, void_t
and the like. Therefore deleteMe
is actually a member function template.
deleteMeToo
is not a function template (or am I wrong here?).
Live example:
https://godbolt.org/z/rGzx8dz3f
Are there any situations when deleteMe
and deleteMeToo
actually behave differently and should one care when preferring trailing requires over the leading one?
when preferring trailing requires over the leading one?
template <..> requires (..) void foo();
is equivalent to
template <..> void foo() requires (..);
So it is just style (or possibly convenience to use function parameter in requires
).
Non template function only allows requires
after.
Are there any situations when
deleteMe
anddeleteMeToo
actually behave differently and should one care?
One is template function whereas the other is not (they are still template entities, from the class).
And template and non-template functions have some differences. Template has/allows
For migration, there are no points to use template (which was added only for SFINAE), so
// C++17
template<typename T>
struct IAmEmpty
{
template <typename U=T, std::enable_if_t<some_condition_v<U>>* = nullptr>
auto deleteMe()
{}
};
would become
// C++20
template<typename T>
struct IAmEmpty
{
auto deleteMe() requires some_condition_v<T> {}
};
You might consider to turn traits some_condition_v
into concepts to allow subsumption.