我似乎无法使用enable_if实例化类专业化

时间:2015-09-01 02:44:48

标签: c++ templates c++11 sfinae enable-if

我正在尝试创建一个具有3个可能版本的模板方法实现的类,具体取决于模板类型是三个“类型集”之一。此外,我正在尝试将这些对象的实例保留在地图中。

所以我最初的尝试是:

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() {
        //...
    }
};

但是获得相同的链接器错误。

1 个答案:

答案 0 :(得分:1)

声明没有参数的函数的语法foo(void)严格用于C兼容性。当void来自模板替换时,它不起作用。 (您实际上并未获得void,但根据评论,这是您的意图。)

SFINAE仅适用于立即声明中的模板参数。您无法使用类模板的参数禁用成员函数。 (这条规则非常令人烦恼,但即使是大量的概念建议都没有建议改变它。)

要根据类模板参数禁用成员,您需要添加伪模板参数,然后使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最黑暗的角落并且活着讲述了这个故事!