zoukankan      html  css  js  c++  java
  • STL中的Concept和Boost库的Concept_Check

    在Generic Programming当中,一个重要的概念就是Concept(滑稽的是,如果把这个Concept也翻译成“概念”,那就狗屁不通了。我倾向于说它是 “操作集”)。Concept就是一组操作,如果一个type具有这些操作,那么就说这个type是这个Concept的一个model。
    这其中 的思想有那么一丁点像是OO当中的interface,一个class如果实现了一个interface,那么它就可以被当作这个interface来 用。同样,如果一个type是一个Concept的model,那么所有接受这个Concept的操作也就可以接受这个type。

    例如, 在STL中,stable_sort这个算法必须接受RandomAccessIterator,这里RandomAccessIterator就是一个 Concept,它规定自己的model必须可以进行下标运算,那么不满足这个Concept的type就无法被编译器接受(搞笑的是我在VC71里面把 list<int> 的iterator传给stable_sort,它居然欣然接受,要知道list的iterator应该只是一个 BidirectionalIterator 啊,比RandomAccessIterator弱多了。不过仔细看看代码,发现VC71的stable_sort接受 BidirectionalIterator就够了,不知道是好还是坏)。在gcc下面如果传递一个BidirectionalIterator给 stable_sort,会得到一堆不知所云的错误提示,让人摸不着头脑。

    C++语言本身并没有对于Concept的直接支持,STL解 决这个问题的办法是用了一些traits来限制iterator的特性,以达到在编译时期检查Concept的目的。但是traits导致的编译错误提示 实在是太可怕了,我非常怀疑有哪个正常人可以从这些错误提示推测出自己错在哪里。

    在boost库里面,提供了一个 ConceptCheck库,它可以帮助我们写出带有Concept检查的代码,而且没有运行时的开销,一旦用户违反Concept限制,输出的错误提示 也比较好懂。举个例子先,如果STL里面有ConceptCheck,那么它的stable_sort大约会这样:

    #include <boost/concept_check.hpp>

    template <class RandomAccessIter>
    void stable_sort(RandomAccessIter first, RandomAccessIter last)
    {
        function_requires< RandomAccessIteratorConcept<RandomAccessIter> >();
        //... bla bla bla......
    }

    有了这个 function_requires ,如果再传递给它list的iterator,编译器(VC71)就会报这样的错:


    c:/boost_1_31_0/boost/concept_check.hpp(642): error C2676: 二进制“+=” : “std::list<_Ty>::iterator”不定义该运算符或到预定义运算符可接收的类型的转换
            with
            [
                _Ty=int
            ]

    当然还有很多别的,但是至少它说了一点:传入的iterator不满足某个运算。这对于用户来说,应当是一个很有用的提示。
    使用Concept Check还有一个额外的好处,那就是调用一个 function_requires可远比写一些traits容易,而且代码也清晰好维护。

    这个好用的 function_requires 就定义在concept_check.hpp当中:
    template <class Concept>
    inline void function_requires(mpl::identity<Concept>* = 0)
    {
    #if !defined(NDEBUG)
      void (Concept::*x)() = BOOST_FPTR Concept::constraints;
      ignore_unused_variable_warning(x);
    #endif
    }

    换句话说,function_requires只在Debug中起作用,那么是不是在Debug当中它就添加了overhead呢?其实也没有,仔细看看代码:
    void (Concept::*x)() = BOOST_FPTR Concept::constraints;
    这 一句取constraints的地址。妙就妙在它让编译器“注意到”constraints,但又没有真正调用constraints,而 constraints是一个虚函数,做实际的check。例如在RandomAccessIteratorConcept(检查iterator是否符 合RandomAccessIterator的Concept)当中,constraint是这个样子:

    template <class TT>
    struct RandomAccessIteratorConcept
    {
        void constraints() {
        function_requires< BidirectionalIteratorConcept<TT> >();
        function_requires< ComparableConcept<TT> >();
    #ifndef BOOST_NO_STD_ITERATOR_TRAITS
        typedef typename std::iterator_traits<TT>::iterator_category C;
        function_requires< ConvertibleConcept< C,
            std::random_access_iterator_tag> >();
        typedef typename std::iterator_traits<TT>::reference R;
    #endif

        i += n;             // require assignment addition operator
        i = i + n; i = n + i; // require addition with difference type
        i -= n;             // require assignment subtraction operator
        i = i - n;                  // require subtraction with difference type
        n = i - j;                  // require difference operator
        (void)i[n];                 // require element access operator
        }
        TT a, b;
        TT i, j;
    #ifndef BOOST_NO_STD_ITERATOR_TRAITS
        typename std::iterator_traits<TT>::difference_type n;
    #else
        std::ptrdiff_t n;
    #endif
    };

    从这个实现中我们完全可以读出RandomAccessIterator的具体含义:
    1. 它必须是一个BidirectionalIterator
    2. 它必须满足“可比较 (Comparable)”的Concept
    3. 它还必须满足“可转换 (Convertible)”的Concept,而且是转换成自己的iterator_category类别
    4. 它必须定义了reference这个type
    5. 这是最重要的,它必须有difference_type,而且可以进行 +, -, +=, -= 的运算
    还要记得,由于这个函数没有真正的被调用过,所以无论你怎么写,它都不会变成实际的代码,所以也不会影响运行效率的!

    boost库除了提供了一系列的Concept Check以外,也鼓励我们自己写Concept Check,至于写法,从这个RandomAccessIteratorConcept的写法,大家也可以看出些端倪了:很简单的。

  • 相关阅读:
    02 _ 该如何选择消息队列
    封装、抽象、继承、多态分别可以解决哪些编程问题?
    04 _ 理论一:当谈论面向对象的时候,我们到底在谈论什么?
    03 _ 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
    接口使用
    结构体和方法
    通道的高级玩法
    通道的基本操作
    极客时间 mp3提取
    iOS多线程中的单例
  • 原文地址:https://www.cnblogs.com/androidme/p/2861859.html
Copyright © 2011-2022 走看看