То, что я хочу, это использовать std :: enable_if или эквивалентный ему, чтобы предотвратить появление исключений в конструкторе и обнаружить во время компиляции такие ошибки.
Я перепробовал много переполнений стека и чужих сайтов std::enable_if
примеров, но я с трудом понимаю, что на самом деле делаю, и всегда заканчиваю ошибкой компиляции.
Проблема с std::enable_if
(и SFINAE, в целом) заключается в том, что он работает только с проверкой параметров шаблона. Поэтому можно включать / отключать полный класс с помощью теста над параметром шаблона класса, но нельзя включать / отключать отдельный метод с помощью теста над параметром шаблона класса.
Если вы хотите, чтобы SFINAE включал / отключал метод (например, ваши конструкторы), вы должны сделать его методом шаблона и протестировать параметр шаблона самого метода.
Так что вы не можете написать что-то как
template <typename = std::enable_if_t<T == A>>
Queue (int)
{ } // only usable when T = A
потому что T
является параметром шаблона класса, а не конструктора.
Но есть хитрость: вы можете использовать значения / типы по умолчанию для параметров шаблона; поэтому следующий код работает
template <QueueType U = T, typename = std::enable_if_t<U == A>>
Queue (int)
{ } // only usable when T = A
потому что проверяется значение U
, которое является параметром шаблона конструктора.
Чтобы включить второй конструктор, только если T
равен B
или C
, вы можете написать
template <QueueType U = T, typename = std::enable_if_t<(U == B) || (U == C)>>
Queue (unsigned, unsigned)
{ } // only usable when T = B || T = C
Ниже приведен полный пример компиляции
#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
}