TransWikia.com

enable_if, SFINAE and template parameters?

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;
}

2 Answers

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:

  • type template parameters
  • non-type template parameters

The name of the template parameter is optional

A template parameter can have a default value

Type template parameter

named, non-defaulted

template <class T>
struct X {};

Usage:

X<int> x{};

named, defaulted

template <class T = int>
struct X {};

Usage:

X<> x1{};
X<char> x2{};

unnamed non-defaulted

template <class>
struct X {};

Usage:

X<int> x{};

unnamed, defaulted

template <class = int>
struct X {};

Usage:

X<> x1{};
X<char> x2{};

Non-type template parameter

named, non-defaulted

template <int I>
struct X {};

Usage:

X<24> x{};

named, defaulted

template <int I = 24>
struct X {};

Usage:

X<> x1{};
X<11> x2{};

unnamed, non-defaulted

template <int>
struct X {};

Usage:

X<11> x{};

unnamed, defaulted

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

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP