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>;
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:
Are there any situations when deleteMe
and deleteMeToo
actually behave differently and should one care when preferring trailing requires over the leading one?
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./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./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?
1 Answer
Reset to default 3when 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
- two-phase lookup
- point of Instantiation
- overloads which differ only by their return type
- ...
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.
template <..> requires (..) void foo();
is equivalent totemplate <..> void foo(); requires (..)
. See Requires_clauses – Jarod42 Commented Mar 31 at 7:41One is function template (with non deducible type) and the other not
OK, so both are functionally equivalent? I am asking in context of reworking C++17 SFINAE code into C++20. Edited question for more context. – alagner Commented Mar 31 at 8:16void deleteMe() = delete;
(which works since C++11) (or the requirement was just an example).= delete
can be applied to non-special methods too. – Jarod42 Commented Mar 31 at 8:36