出于性能方面的考虑,我使用带有枚举的模板化类,而不是继承继承(这不是一个选择)。
在这一点上,我有类似的东西:
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
}
现在,我在T
上使用大量烦人的ifs / switchs,如果为已定义的T
调用了不兼容的构造函数,则会出现异常。
我想要的是使用std::enable_if
或等效名称来防止在构造函数上引发异常并在编译时检测此类错误。
我已经尝试了很多堆栈溢出和外部站点std::enable_if
的示例,但是我几乎无法理解我在做什么,而且我总是以编译错误告终。
在此先感谢您,并很抱歉提出了一个可能很简单的问题。我对模板没有知识。
环境:Linux GCC 8和c ++ 14 限制:没有虚拟方法的最高性能。
答案 0 :(得分:6)
我想要的是使用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
}
答案 1 :(得分:3)
现在我正在使用大量烦人的if / t切换,如果为定义的T调用了不兼容的构造函数,则会出现异常。
所以看来您不需要构造函数对SFINAE友好,因此<__main__.bag object at 0x004D0830> ['pencil', 'book']
<__main__.bag object at 0x004D0790> ['pencil', 'book']
似乎足够:
static_assert
答案 2 :(得分:2)
静态断言没问题-但您可以删除所有枚举值的这些构造函数-除了要提供的值:
template <QueueType T>
class Queue
{
public:
Queue(int a) = delete; // only usable when T = A
//other constructors
Queue(unsigned a, unsigned b) = delete; // only usable when T = B || T = C
Queue(somestruct z) = delete; // only usable when T = B || T = C
private:
// not necessary - but allows to have a little less code
struct EnablerType {};
static constexpr EnablerType Enabler{};
Queue(unsigned a, unsigned b, EnablerType) { }// only usable when T = B || T = C
Queue(somestruct z, EnablerType) { } // only usable when T = B || T = C
};
现在-显式启用:
template <>
inline Queue<A>::Queue(int a) {}
template <>
inline Queue<B>::Queue(unsigned a, unsigned b) : Queue(a, b, Enabler) {}
template <>
inline Queue<C>::Queue(unsigned a, unsigned b) : Queue(a, b, Enabler) {}
template <>
inline Queue<B>::Queue(somestruct z) : Queue(z, Enabler) {}
template <>
inline Queue<C>::Queue(somestruct z) : Queue(z, Enabler) {}
与static_assert
相比,一大优势是您可以检查Queue
是否由给定的参数集构造而成(因此您可以进行进一步的SFINAE):
int main() {
static_assert(std::is_constructible_v<Queue<A>, int>, "failed");
static_assert(!std::is_constructible_v<Queue<B>, int>, "failed");
...
}