I have the following example of trying to use enable_if
for instantiating 2 different specializations of the class A
, but I do not understand it correctly as they are not getting invoked, any suggestions ?
#include <type_traits>
#include <iostream>
class SC1 {
public:
SC1() {
std::cout << "SC1\n";
}
};
class SC2 {
public:
SC2() {
std::cout << "SC2\n";
}
void f() {};
};
template <class T, typename TC1 = void, class TC2 = void>
class A {
public :
A() {
std::cout << "default A\n";
};
};
template <class T>
class A <T, typename std::enable_if_t<T::SC1_ENABLE, typename T::EN>, void> : public T::EN {
static_assert(!std::is_same<typename T::EN, SC1>::value);
public:
A() {
std::cout << "1 type is " << typeid(typename std::enable_if_t<T::SC1_ENABLE>).name() << "\n";
}
};
template <typename T>
class A <T, void, typename std::enable_if_t<T::SC2_ENABLE, typename T::EN>> : public T::EN {
public:
A() {
static_assert(std::is_same<typename T::EN, SC2>::value);
//std::cout << "2 type is " << typeid(typename std::enable_if_t<T::SC2_ENABLE>).name() << "\n";
}
};
struct T1 {
static constexpr bool SC1_ENABLE = false;
static constexpr bool SC2_ENABLE = false;
};
struct T2 {
static constexpr bool SC1_ENABLE = true;
static constexpr bool SC2_ENABLE = false;
typedef SC1 EN;
};
struct T3 {
static constexpr bool SC2_ENABLE = true;
static constexpr bool SC1_ENABLE = false;
typedef SC2 EN;
};
int main() {
// std::enable_if_t<T3::SC2_ENABLE,T3::EN>();
A<T3>();
A<T2>();
}
I have tried different variants of it, compiled it with c++ std 20 using GCC and only the default implementation of A
gets invoked.
You're using std::enable_if
wrong.
In the primary template, you're using typename TC1 = void
for the template parameter (which could be simplified to typename = void
by the way).
For A<T2>()
(equivalent to A<T2, void, void>()
) to use the first partial specialization, the partial specialization must also have void
there.
However, you are using typename std::enable_if_t<T::SC1_ENABLE, typename T::EN>
, which is the type T::EN
after substitution, so this will never match.
It works once you write:
std::enable_if_t<T::SC1_ENABLE>
Note that void
is the default argument for the second template parameter, and typename
is unnecessary.
However, this would be dropping the requirement that T::EN
has to be a type, so you could write something like:
template <class T>
class A <T, std::enable_if_t<T::SC1_ENABLE, std::void_t<typename T::EN>, void>
: public T::EN { /* ... */ };
Also keep in mind that having two separate void
s like that for different requirements does not impose some ordering between them, and this will likely blow up in your face.
See Reliable way of ordering mulitple std::void_t partial specializations for type traits for reliable solutions.
If you're compiling with C++20, you should also use C++20 features; namely, concepts and constraints:
template <typename T>
concept T2_like = T::SC1_ENABLE && requires { typename T::EN; };
template <class T> // primary template
struct A {};
template <T2_like T> // partial specialization with type-constraint for T
struct A<T> {};