Stack Overflow Asked on November 16, 2021
I’m trying to understand enable_if
and SFINAE. So far, my use of templates has been explicitly passed template arguments to the template parameters or deduction of template parameters during instantiation. I’m confused in the code below how the member of enable_if<>::type (typedef int type) can be a template parameter. I.e. the parameter list becomes <class T = int, int* = nullptr>
. I would expect a parameter like this to be int Z = nullptr
.
Does anyone know what I’m missing in regards to understanding why these template parameters assigned by enable_if
work?
Thanks
#include <type_traits>
#include <iostream>
#if 1
template <class T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void do_stuff(T& t) {
std::cout << "do_stuff integraln";
}
#endif
#if 0 // understood
template <class T>
typename std::enable_if<std::is_integral<T>::value,
void>::type
do_stuff(T& t) {
std::cout << "do_stuff integraln";
}
#endif
struct S {};
int main(int argc, char *argv[])
{
int i = 1;
do_stuff(i);
//do_stuff<S>(S{});
return 0;
}
The parameter is anonymous. The type and default is specified, but there's nothing to actually refer to it later.
template<typename> struct A; // fine
template<int> struct B; // fine
template<typename F, F = nullptr> struct C; // fine
using AI = A<int>;
using BI = B<5>;
using CI = C<int, 5>;
using CD = C<void*>; // all fine
using Oops = C<char>; // not fine
Your case just replaces the simpler types like int
and F
with a big honking typename std::enable_if<...>::type
. We don't care about the actual argument that gets passed it ends up being, so that's why it's unnamed.
template <class T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void do_stuff(T &t) {
std::cout << "do_stuff integraln";
}
Plugging in T = int
, typename std::enable_if<std::is_integral<T>::value>::type = void
, so the second defaulted parameter just looks like void* = nullptr
, which means it's an unnamed parameter of type void*
that defaults to nullptr
. Note that the typename
is part of the type of a non-type template parameter. It is not serving in its other capacity as the introducer of type template parameters. Also, please note that the modern C++ way to write this is
// typename instead of class is more a style thing
template<typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
where enable_if_t
and is_integral_v
are aliases that abbreviate all the ::value
and typename ...::type
nonsense:
namespace std {
template<typename T>
constexpr inline bool is_integral_v = std::is_integral<T>::value;
template<bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
}
Answered by HTNW on November 16, 2021
There are two kinds of template parameters:
The name of the template parameter is optional
A template parameter can have a default value
template <class T>
struct X {};
Usage:
X<int> x{};
template <class T = int>
struct X {};
Usage:
X<> x1{};
X<char> x2{};
template <class>
struct X {};
Usage:
X<int> x{};
template <class = int>
struct X {};
Usage:
X<> x1{};
X<char> x2{};
template <int I>
struct X {};
Usage:
X<24> x{};
template <int I = 24>
struct X {};
Usage:
X<> x1{};
X<11> x2{};
template <int>
struct X {};
Usage:
X<11> x{};
template <int = 24>
struct X {};
Usage:
X<> x1{};
X<11> x2{};
For SFINAE the technique requires a default value. Since you don't care for the parameter inside the class you can skip its name. As for the type or non-type you can chose which one is easier to write in your particular case.
Answered by bolov on November 16, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP