Trying to learn how to use Eric Niebler's ranges-v3 library, and reading the source code, I saw that macro definition:
#define CONCEPT_PP_CAT_(X, Y) X ## Y
#define CONCEPT_PP_CAT(X, Y) CONCEPT_PP_CAT_(X, Y)
/// \addtogroup group-concepts
/// @{
#define CONCEPT_REQUIRES_(...) \
int CONCEPT_PP_CAT(_concept_requires_, __LINE__) = 42, \
typename std::enable_if< \
(CONCEPT_PP_CAT(_concept_requires_, __LINE__) == 43) || (__VA_ARGS__), \
int \
>::type = 0 \
/**/
So, in short, a template definition like:
template<typename I, typename O,
CONCEPT_REQUIRES_(InputIterator<I>() &&
WeaklyIncrementable<O>())>
void fun_signature() {}
is translated as:
template<typename I, typename O,
int a_unique_name = 42,
typename std::enable_if
<false || (InputIterator<I>() &&
WeaklyIncrementable<O>()), int>::type = 0
>
void fun_signature() {}
I would like to know why is that macro implement that way. Why is that integer needed, and why does it need a false || cond
and not just a cond
template argument?
a template definition like ... is translated as ...
Close. It actually translates as:
template<typename I, typename O,
int a_unique_name = 42,
typename std::enable_if
<a_unique_name == 43 || (InputIterator<I>() &&
WeaklyIncrementable<O>()), int>::type = 0
>
void fun_signature() {}
The uniquely named int
is there in order to ensure that the condition for enable_if
is dependent on a template parameter, to avoid the condition being checked at template definition time instead of at instantiation time so that SFINAE can happen. Consider this class definition:
template<class T>
struct S {
template<class U, CONCEPT_REQUIRES_(ranges::Integral<T>())>
void f(U);
};
without the injected-unique-int
, this definition would lower to:
template<class T>
struct S {
template<class U, std::enable_if_t<ranges::Integral<T>()>>
void f(U);
};
and since ranges::Integral<T>()
isn't dependent on a parameter of this function template, compilers will diagnose that std::enable_if_t<ranges::Integral<T>()>
- which lowers to typename std::enable_if<ranges::Integral<T>()>::type
- is ill-formed because std::enable_if<false>
contains no member named type
. With the
injected-unique-int
, the class definition lowers to:
template<class T>
struct S {
template<class U, int some_unique_name = 42,
std::enable_if_t<some_unique_name == 43 || ranges::Integral<T>()>>
void f(U);
};
now a compiler cannot perform any analysis of the enable_if_t
at template definition time, since some_unique_name
is a template parameter that might be specified as 43
by a user.
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