Search code examples
c++boostc++11c++-concepts

What's the difference between C++0x concepts and The Boost Concept Check Library (BCCL)?


Concepts didn't make the C++0x standard, but Boost still provides The Boost Concept Check Library (BCCL). I guess that BCCL doesn't cover everything that was meant to into the C++0x standard. What is the difference between BCCL and the proposed C++0x solution?


Solution

  • Checking the template definition

    A big difference of concepts to these manual solutions is that concepts allow the definition of a template to be type-checked without doing anything special. The concept check library allows only the *use* of it to be type checked (unless you manually write test-instantiation types or make use of provided types for standard cases, see below). Example:
    template<typename InputIterator>
    int distance(InputIterator a, InputIterator b) 
    { return b - a; }
    

    You may now sprinkle that template with concept checks and traits, but you will never get an error after writing that template - because the Standard allows the compiler to delay compiling the template until instantiation. For checking, you have to write "archetype" classes, which contain exactly those operations that are required by the interface and then instantiate them artificially.

    Reading the documentation of BCCL, i found it already includes the common archetypes like "default constructible". But if you write your own concepts, you will have to also provide your own archetypes, which isn't easy (you have to find exactly the minimal functionality a type has to provide). For example, if your archetype contains a operator-, then the test of your template with that (incorrect) archetype will succeed, although the concepts don't require such an operator.

    The rejected concepts proposal creates archetypes for you automatically, based on the requirements that were specified and that were implied (a pointer type T* used in a parameter will imply the PointeeType requirement for T, for example). You don't have to care about this stuff - except of course when your template definition contains a type error.

    Checking semantic requirements

    Consider this code, using hypothetical concept checks

    template<ForwardIterator I>
    void f(I a, I b) {
      // loop two times!
      loopOverAToB(a, b);
      loopOverAToB(a, b);
    }
    

    The BCCL manual says that semantic requirements are not checked. Only syntax requirement and types are checked. Consider a forward iterator: There exists the semantic requirement that you may use it in multi-pass algorithms. Syntax-checking only won't be able to test this requirement (think about what happens if a stream iterator accidentally would pass that check!)

    In the rejected proposal, you had to explicitly put auto in front of concept definitions to make the compiler flag success after syntax-checking. If auto wasn't specified, then a type explicitly had to define a concept map to say it supports that concept. A stream iterator would thus never be taken to pass a ForwardIterator check.

    Syntax remapping

    This was another feature. A template such as
    template<InputIterator I>
      requires OutputStreamable<I::value_type>
    void f(I a, I b) {
      while(a != b) std::cout << *a++ << " ";
    }
    

    Can be used like the following, if the user would provide a concept map that teaches the compiler how to dereference an integer, and thus how an integer satisfies the InputIterator concept.

    f(1, 10);
    

    This is the benefit of a language-based solution, and cannot be solved by BCCL ever, i believe.

    Concept based Overloading

    On a quick read of BCCL, i can also not spot anything that allows this to happen. A concept matching failure seems to cause a hard compilation error. The rejected proposal allows the following:
    template<ForwardIterator I>
    I::difference_type distance(I a, I b) {
      I::difference_type d = 0; while(a != b) ++a, ++d;
      return d;
    }
    
    template<RandomAccessIterator I>
    I::difference_type distance(I a, I b) {
      return b - a;
    }
    

    If a type could be used with both templates, then the second template would be used, because it's more specialized: RandomAccessIterator refines the ForwardIterator concept.