我似乎无法用 enable_if 实例化 class 专业化
I can't seem to instantiate class specializations with enable_if
我正在尝试制作一个 class,它具有 3 个可能的模板方法实现版本,具体取决于模板类型是三个 'type sets' 之一。另外,我试图将这些对象的实例保留在地图中。
所以我最初的尝试是:
class DescriptorType {
public:
int id;
DescriptorType() : id(-1) {}
virtual ~DescriptorType() {}
};
template <typename T>
class Descriptor : public DescriptorType {
public:
T value;
void update(TYPE_CONSTRAINT((TYPE(int) || TYPE(float)))) {
// specific implementation for these types
}
void update(TYPE_CONSTRAINT((TYPE(vec2) || TYPE(vec3) || TYPE(vec4)))) {
// specific implementation for these types
}
void update(TYPE_CONSTRAINT((TYPE(mat2) || TYPE(mat3) || TYPE(mat4)))) {
// specific implementation for these types
}
};
(我有一个包含以下内容的包含文件——用于宏):
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
#define TYPE_CONSTRAINT(x) enable_if_t<x, T>
#define TYPE(x) std::is_same<x, T>::value
但是好像不行。它可以编译,但是在我尝试为任何 T 实例化 Descriptor<T>
的那一刻,无论是通用 T 还是像 'int' 这样的具体类型,我都会收到所有可能类型的链接器错误(在我的例子中是 16)。
我猜我用错了 enable_if
。但我不确定如何。
更新:
我也尝试过这种方法,结果相同:
template <typename T, class Enable = void>
class Descriptor : public DescriptorType {
public:
T value;
void update();
};
template <typename T>
class Descriptor<T, TYPE_CONSTRAINT((TYPE(int) || TYPE(float)))> : public DescriptorType {
public:
T value;
void update() {
//...
}
};
template <typename T>
class Descriptor<T, TYPE_CONSTRAINT((TYPE(vec2) || TYPE(vec3) || TYPE(vec4)))> : public DescriptorType {
public:
T value;
void update() {
//...
}
};
template <typename T>
class Descriptor<T, TYPE_CONSTRAINT((TYPE(mat2) || TYPE(mat3) || TYPE(mat4)))> : public DescriptorType {
public:
T value;
void update() {
//...
}
};
但得到相同的链接器错误。
用于声明不带参数的函数的语法 foo(void)
严格用于 C 兼容性。当 void
来自模板替换时,它不起作用。 (你实际上并没有得到 void
,但根据评论,这是你的意图。)
SFINAE 仅适用于直接声明中的模板参数。您不能使用 class 模板的参数禁用成员函数。 (这条规则很烦人,但即使是海量的概念提案也没有建议改变它。)
要禁用基于class 模板参数的成员,您需要添加一个伪造的模板参数,然后使SFINAE 看起来依赖它。 SFINAE 也可以安全地放在模板参数默认参数中:
#define TYPE_CONSTRAINT(x) enable_if_t< (bogus(), x) >
template< typename bogus = void,
typename = TYPE_CONSTRAINT((TYPE(int) || TYPE(float))) >
void update() {
仍然存在过载问题。 SFINAE 发生在重载解析时,但会在声明重载时检查它们的相互兼容性。单靠 SFINAE 不足以保证不同功能的兼容性,因此您还需要破坏该安全机制。
#define TYPE_CONSTRAINT(x) enable_if_t< x, bogus >
template< typename bogus = void,
void * = (TYPE_CONSTRAINT((TYPE(int) || TYPE(float))) *) nullptr >
void update() …
template< typename bogus = int, // Use different types here
int * = (TYPE_CONSTRAINT((TYPE(vec2) || TYPE(vec3))) *) nullptr >
void update() …
template< typename bogus = char,
char * = (TYPE_CONSTRAINT((TYPE(mat2) || TYPE(mat3))) *) nullptr >
void update() …
http://coliru.stacked-crooked.com/a/64e957e0bb29cde9
说真的,这是解决方法。从好的方面来说,你已经探访了 SFINAE 最黑暗的角落,并活着讲述了这个故事!
我正在尝试制作一个 class,它具有 3 个可能的模板方法实现版本,具体取决于模板类型是三个 'type sets' 之一。另外,我试图将这些对象的实例保留在地图中。
所以我最初的尝试是:
class DescriptorType {
public:
int id;
DescriptorType() : id(-1) {}
virtual ~DescriptorType() {}
};
template <typename T>
class Descriptor : public DescriptorType {
public:
T value;
void update(TYPE_CONSTRAINT((TYPE(int) || TYPE(float)))) {
// specific implementation for these types
}
void update(TYPE_CONSTRAINT((TYPE(vec2) || TYPE(vec3) || TYPE(vec4)))) {
// specific implementation for these types
}
void update(TYPE_CONSTRAINT((TYPE(mat2) || TYPE(mat3) || TYPE(mat4)))) {
// specific implementation for these types
}
};
(我有一个包含以下内容的包含文件——用于宏):
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
#define TYPE_CONSTRAINT(x) enable_if_t<x, T>
#define TYPE(x) std::is_same<x, T>::value
但是好像不行。它可以编译,但是在我尝试为任何 T 实例化 Descriptor<T>
的那一刻,无论是通用 T 还是像 'int' 这样的具体类型,我都会收到所有可能类型的链接器错误(在我的例子中是 16)。
我猜我用错了 enable_if
。但我不确定如何。
更新:
我也尝试过这种方法,结果相同:
template <typename T, class Enable = void>
class Descriptor : public DescriptorType {
public:
T value;
void update();
};
template <typename T>
class Descriptor<T, TYPE_CONSTRAINT((TYPE(int) || TYPE(float)))> : public DescriptorType {
public:
T value;
void update() {
//...
}
};
template <typename T>
class Descriptor<T, TYPE_CONSTRAINT((TYPE(vec2) || TYPE(vec3) || TYPE(vec4)))> : public DescriptorType {
public:
T value;
void update() {
//...
}
};
template <typename T>
class Descriptor<T, TYPE_CONSTRAINT((TYPE(mat2) || TYPE(mat3) || TYPE(mat4)))> : public DescriptorType {
public:
T value;
void update() {
//...
}
};
但得到相同的链接器错误。
用于声明不带参数的函数的语法 foo(void)
严格用于 C 兼容性。当 void
来自模板替换时,它不起作用。 (你实际上并没有得到 void
,但根据评论,这是你的意图。)
SFINAE 仅适用于直接声明中的模板参数。您不能使用 class 模板的参数禁用成员函数。 (这条规则很烦人,但即使是海量的概念提案也没有建议改变它。)
要禁用基于class 模板参数的成员,您需要添加一个伪造的模板参数,然后使SFINAE 看起来依赖它。 SFINAE 也可以安全地放在模板参数默认参数中:
#define TYPE_CONSTRAINT(x) enable_if_t< (bogus(), x) >
template< typename bogus = void,
typename = TYPE_CONSTRAINT((TYPE(int) || TYPE(float))) >
void update() {
仍然存在过载问题。 SFINAE 发生在重载解析时,但会在声明重载时检查它们的相互兼容性。单靠 SFINAE 不足以保证不同功能的兼容性,因此您还需要破坏该安全机制。
#define TYPE_CONSTRAINT(x) enable_if_t< x, bogus >
template< typename bogus = void,
void * = (TYPE_CONSTRAINT((TYPE(int) || TYPE(float))) *) nullptr >
void update() …
template< typename bogus = int, // Use different types here
int * = (TYPE_CONSTRAINT((TYPE(vec2) || TYPE(vec3))) *) nullptr >
void update() …
template< typename bogus = char,
char * = (TYPE_CONSTRAINT((TYPE(mat2) || TYPE(mat3))) *) nullptr >
void update() …
http://coliru.stacked-crooked.com/a/64e957e0bb29cde9
说真的,这是解决方法。从好的方面来说,你已经探访了 SFINAE 最黑暗的角落,并活着讲述了这个故事!