在某些枚举模板情况下启用 class 构造函数
Enable class constructor in some enumerated template cases
出于性能原因,我使用带有枚举的模板化 class 而不是继承继承(这不是一个选项)。
此时我有类似的东西:
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
.
调用不兼容的构造函数,我会在 T
上使用令人讨厌的 ifs/switches 数量,并且会增加异常
我想要的是使用 std::enable_if
或等价物来防止在构造函数上抛出异常并在编译时检测此类错误。
我试过很多堆栈溢出和国外网站的例子std::enable_if
,但我几乎无法理解我到底在做什么,而且总是以编译错误告终。
提前致谢,很抱歉提出一个可能回答不多的问题。我对模板一窍不通。
环境:Linux GCC 8 和 c++14
限制:没有虚拟方法的最大性能。
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.
所以你的构造函数似乎不需要对 SFINAE 友好,所以 static_assert
似乎就足够了:
template <QueueType T>
class Queue {
public:
Queue(int a)
{
static_assert(T == A, "!");
// ...
}
Queue(unsigned a, unsigned b)
{
static_assert(T == B || T == C, "!");
// ...
}
Queue(somestruct z)
{
static_assert(T == B || T == C, "!");
// ...
}
//...
};
静态断言很好 - 但您可以为所有枚举值删除这些构造函数 - 除了您要提供的那个:
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");
...
}
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.
std::enable_if
(和 SFINAE,更普遍)的问题是它只能检查模板参数。 enable/disable 一个完整的 class 也可以,对 class 的模板参数进行测试,但不能 enable/disable 单个方法,对模板参数进行测试class.
的
如果你想要 SFINAE enable/disable 一个方法(就像你的构造函数),你必须把它变成一个模板方法并测试方法本身的模板参数。
所以你不能写成
template <typename = std::enable_if_t<T == A>>
Queue (int)
{ } // only usable when T = A
因为 T
是 class 的模板参数,而不是构造函数的模板参数。
但是有一个技巧:模板参数可以使用默认的values/types;所以下面的代码有效
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
}
出于性能原因,我使用带有枚举的模板化 class 而不是继承继承(这不是一个选项)。
此时我有类似的东西:
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
.
T
上使用令人讨厌的 ifs/switches 数量,并且会增加异常
我想要的是使用 std::enable_if
或等价物来防止在构造函数上抛出异常并在编译时检测此类错误。
我试过很多堆栈溢出和国外网站的例子std::enable_if
,但我几乎无法理解我到底在做什么,而且总是以编译错误告终。
提前致谢,很抱歉提出一个可能回答不多的问题。我对模板一窍不通。
环境:Linux GCC 8 和 c++14 限制:没有虚拟方法的最大性能。
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.
所以你的构造函数似乎不需要对 SFINAE 友好,所以 static_assert
似乎就足够了:
template <QueueType T>
class Queue {
public:
Queue(int a)
{
static_assert(T == A, "!");
// ...
}
Queue(unsigned a, unsigned b)
{
static_assert(T == B || T == C, "!");
// ...
}
Queue(somestruct z)
{
static_assert(T == B || T == C, "!");
// ...
}
//...
};
静态断言很好 - 但您可以为所有枚举值删除这些构造函数 - 除了您要提供的那个:
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");
...
}
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.
std::enable_if
(和 SFINAE,更普遍)的问题是它只能检查模板参数。 enable/disable 一个完整的 class 也可以,对 class 的模板参数进行测试,但不能 enable/disable 单个方法,对模板参数进行测试class.
如果你想要 SFINAE enable/disable 一个方法(就像你的构造函数),你必须把它变成一个模板方法并测试方法本身的模板参数。
所以你不能写成
template <typename = std::enable_if_t<T == A>>
Queue (int)
{ } // only usable when T = A
因为 T
是 class 的模板参数,而不是构造函数的模板参数。
但是有一个技巧:模板参数可以使用默认的values/types;所以下面的代码有效
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
}