1

我正在尝试使用 C++11,而不是使用 C++ 作为 C++98(我来自 C)并且我已经达到了类型特征,而不是跳入我认为我会尝试的标准解决这个问题。

通常我会使用继承来添加基于类型的方法,并依赖于用户,但我想使用特征,现在我不希望最终用户使用我的“自定义”,这是因为它是一个实验.

我首先创建了一个 True 和一个 False 类型,如下所示:

struct True {
    static const bool value = true;
};

struct False {
    static const bool value = false;
};

然后我的enable_if定义,我理解使用结构或类既是结构(在 C 意义上,具有大小等)又是命名空间的事实,如下所示:

template<bool B,class T>
struct EnableIf {};

template<class T>
struct EnableIf<true,T> {
    typedef T type;
};

我现在希望只有EnableIf一个true名为“type”的“命名空间类型成员”(如果可以的话),它是模板 T 的任何内容。这似乎有效。

我知道当我尝试访问时EnableIf<false,T>::type,由于没有type

我很确定到目前为止一切都是正确的,但我只是冗长。

我的测试用例

我选择了一个列表作为我的测试场(再次不使用标准集,因为我正在调查)通常我使用一个类层次结构来做到这一点,我有一个list只能充当数组,唯一的额外成员将是int find(T*);因为 T* 是 T 的身份。

然后我将它扩展为有一个int find(T&); int find(T&,int)andint count(T&)这将使用 == 来比较 Ts。这就是我将其留给用户的意思,他们可以根据/他们/对类型的了解来选择他们想要的列表。

我想使用EnableIf(稍后std::enable_if当我更有信心时)来代替,这样当模板被删除时,功能只有enable在类型能够以这种方式使用时才有效。

列表定义

template<class T>
class List {
public:
    typedef typename T::hasEquality hasEquality;
    virtual ~List() {}
    virtual T& operator[](int index) =0;
    virtual const T& operator[](int index) const =0;
    virtual void append(T*) =0;
    virtual int length() const =0;
    virtual
        typename EnableIf<hasEquality::value, bool>::type
    operator==(const List<T>& rhs) const {
        if(length() == rhs.length()) {
            for(int k=0;k!=length();k++) {
                if(!((*this)[k] == rhs[k])) {
                    return false;
                }
            }
            return true;
        } else {
            return false;
        }
    }
    virtual
        typename EnableIf<T::hasEquality::value, int>::type
        count(const T& what) const =0;
};

这是一个列表而不是一组,所以顺序很重要。您可以看到这应该hasEquality具有传递性,因为:

如果 T 具有相等的概念,则 T 的列表也具有相等的概念

然后我继续实现一个单链表。

测试类型

class A {
public:
    A(int value) { val = value; }
    typedef True hasEquality;
    bool operator==(const A& rhs) const {
        if(val == rhs.val) {
            return true;
        }
        return false;
    }

private:
    int val;
};

class B {
public:
    typedef False hasEquality;
};

结果

int main(int,char**) {
    LinkedList<A> listA;
    listA.append(new A(6));
    A a(6);
    std::cout<<"there are "<<listA.count(a)<<" of them\n";
    return 0;
}

正如你所期望的那样有效。我的第一个测试最初包括 B,但这会导致问题。

int main(int,char**) {
    LinkedList<A> listA;
    listA.append(new A(6));
    A a(6);
    std::cout<<"there are "<<listA.count(a)<<" of them\n";

    LinkedList<B> listB;

    return 0;
}

这没有,它失败了:

src/main.cpp: In instantiation of ‘class List<B>’:
src/main.cpp:77:7:   required from ‘class LinkedList<B>’
src/main.cpp:176:16:   required from here
src/main.cpp:59:2: error: no type named ‘type’ in ‘struct EnableIf<false, bool>’
  operator==(const List<T>& rhs) const {
  ^
src/main.cpp:73:3: error: no type named ‘type’ in ‘struct EnableIf<false, int>’
   count(const T& what) const =0;
   ^
src/main.cpp: In instantiation of ‘class LinkedList<B>’:
src/main.cpp:176:16:   required from here
src/main.cpp:134:3: error: no type named ‘type’ in ‘struct EnableIf<false, int>’
   count(const T& what) const {
   ^
make: *** [build/main.o] Error 1

出于某种原因,它将错误标记放在 typename 行之后,在我使用EnableIfwith的任何地方都不满意false

我真的不知道为什么会这样,没错,没有type,但这是设计使然!

研究

http://en.cppreference.com/w/cpp/types/enable_if

引用:

template<bool B, class T = void>
struct enable_if {};

template<class T>
struct enable_if<true, T> { typedef T type; };

我的只是名称不同,默认的 T 是无效的,将它添加到我的(如我所料)并不能解决问题。

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/SFINAE

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/enable-if

确认我的想法。

奖金问题

constexpr 和静态

最初我尝试过struct False { constexpr bool operator==(bool what) { return !what; } };,对于True;

但这不起作用,我不能使用“静态”这个词来限定 operator==,但我可以使用调用constexpr static bool is(bool what);相同效果的方法,为什么 constexpr 不暗示静态?

在我看来,constexprs 从来没有真正存在过,而且设计有点像虚拟的相反,没有什么说你不能使用实例来调用静态方法,我刚刚检查了 C++03 标准,第 9.4 节证实了这一点,这改变了吗?

SFINAE

是否可以使用 SFINAE 来假设尚未定义的False时间?hasMember我知道这不适用于基本类型等,这是一个实验。在我有信心之前,我不会在生产中使用这些技术。

4

2 回答 2

3

问题在这里:

virtual
    typename EnableIf<T::hasEquality::value, int>::type
    count(const T& what) const =0;

您遇到了另一个示例,其中泛型编程(模板)和面向对象的编程风格发生冲突。

SFINAE 是一种使用模板的元编程技术。尽管出现(使用T),但上面声明的函数不是模板。这是模板类中的一个普通函数。模板类型参数T是 的参数List不是的参数count

例如,以下是 SFINAE 的示例:

template<class T>
class List {
public:
template<class T>
class List {
public:
    // ...
    template <typename U>
    typename EnableIf<std::is_same<U, T>::value && U::hasEquality::value, int>::type
    count(const U& what) const { std::cout << "1\n"; }

    template <typename U>
    typename EnableIf<std::is_same<U, T>::value && !U::hasEquality::value, int>::type
        count(const U& what) const { std::cout << "2\n"; }
};

};

int main() {
    A a(1);
    B b;
    List<A> la;
    la.count(a); // outputs 1
    List<B> lb;
    lb.count(b); // ouputs 2
}

请注意,这两个counts 现在是一个模板(在 上参数化U)。T仅当与 相同类型时,两者才有效U。这是一种仅接受的解决方法T(它并不完美,例如,它会丢弃隐式转换)。此外,第一个要求,U::hasEquality::value == true而第二个要求相反。

这里的关键点是 SFINAE 在模板上工作。

但正如你所看到的,我改变了你的设计并使其成为count非虚拟的。不幸的是,您不能将count上述函数设为虚拟,因为模板函数不能是虚拟的。

基本问题如下。模板函数仅在调用时才被实例化。因此,当编译器解析List(我的版本)时,它还不知道所有实例化count都将存在。

对于每个虚函数,虚表中都应该有一个条目,编译器在解析List时必须知道虚表中有多少条目。

因此,一方面,List编译器在解析时不知道模板实例的数量,另一方面,它必须知道虚函数的数量。结论是模板函数不能是虚函数。

于 2013-10-03T07:49:49.160 回答
2

SFINE 仅适用于模板参数推导和函数的重载解析。对于类,您可以改为使用模板专业化。在你的情况下,你可以做这样的模板专业化(EnableIf不需要):

template <typename T, typename = typename T::hasEquality>
class List;

template <typename T>
class List<T, False> {
    // no operator==
};

template <typename T>
class List<T, True> {
public:
    // ...
    bool operator==(const List<T,True>& rhs) const {
        // ...
    }
    // ...
};

至于您的constexpr问题,您可以拥有constexpr创建编译时对象的构造函数,然后您可以调用constexpr将在编译时运行的成员函数,因此constexpr暗示static.


我现在回答问题似乎为时已晚。您可以使用 SFINAE 有条件地启用成员函数,它必须是模板函数。所以你可以改变你operator==

template <typename = typename EnableIf<T::hasEquality::value, void>::type>
bool operator==(const List<T>& rhs) const {
    // ...
}

它应该可以工作。

于 2013-10-03T07:25:20.850 回答