For performance reasons, I'm using a templated class with an enum instead of relay on inheritance (It is not an option).
At this point I have something like:
typedef enum { A, B, C, D } QueueType;
template <QueueType T> class Queue {
Queue(int a){...} // only usable when T = A
Queue(unsigned a, unsigned b){...} // only usable when T = B || T = C
Queue(somestruct z){...} // only usable when T = B || T = C
//other constructors
}
Now I'm using an annoying amount of ifs/switches over T
and rising exceptions if an incompatible constructor is called for a defined T
.
What I want is to use std::enable_if
or equivalent to prevent throwing exceptions on constructor and detect on compilation time such kind of errors.
I've tried many stack-overflows and foreign sites std::enable_if
examples, but I can barely understand what I'm really doing and I always end on a compilation error.
Thanks in advance and sorry for asking for a probably trivially answered question. I'm noob with templates.
Environment: Linux GCC 8 and c++14 Restrictions: Maximal performance with no virtual methods.
What I want is to use std::enable_if or equivalent to prevent throwing exceptions on constructor and detect on compilation time such kind of errors.
I've tried many stack-overflows and foreign sites
std::enable_if
examples, but I can barely understand what I'm really doing and I always end on a compilation error.
The problem with std::enable_if
(and SFINAE, more in general) is that it works only checking template parameters. So can enable/disable a full class, with a test over a template parameter of the class, but can't enable/disable a single method, with a test over a template parameter of the class.
If you want SFINAE enable/disable a method (like your constructors) you have to made it a template method and test a template parameter of the method itself.
So you can't write something as
template <typename = std::enable_if_t<T == A>>
Queue (int)
{ } // only usable when T = A
because T
is a template parameter of the class, not of the constructor.
But there is a trick: you can use default values/types for template parameters; so the following code works
template <QueueType U = T, typename = std::enable_if_t<U == A>>
Queue (int)
{ } // only usable when T = A
because is checked the value U
that is a template parameter of the constructor.
To enable the second constructor only when T
is B
or C
, you can write
template <QueueType U = T, typename = std::enable_if_t<(U == B) || (U == C)>>
Queue (unsigned, unsigned)
{ } // only usable when T = B || T = C
The following is a full compiling example
#include <type_traits>
typedef enum { A, B, C, D } QueueType;
template <QueueType T>
struct Queue
{
template <QueueType U = T, typename = std::enable_if_t<U == A>>
Queue (int)
{ } // only usable when T = A
template <QueueType U = T, typename = std::enable_if_t<(U == B) || (U == C)>>
Queue (unsigned, unsigned)
{ } // only usable when T = B || T = C
};
int main()
{
Queue<A> qa0{1}; // compile
//Queue<A> qa1{1u, 2u}; // compilation error
// Queue<B> qb0{1}; // compilation error
Queue<B> qb1{1u, 2u}; // compile
// Queue<C> qc0{1}; // compilation error
Queue<C> qc1{1u, 2u}; // compile
// Queue<D> qd0{1}; // compilation error
// Queue<D> qd1{1u, 2u}; // compilation error
}
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