views:

596

answers:

3

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?

+15  A: 

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. 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.

Johannes Schaub - litb
+1 for details :)
Klaim
+4  A: 

The C++0x concept feature would be a core language feature, whose whole process would be done by the compiler.

The Boost Concept Check Library is the almost same feature but written in C++ and macro as a library to simulate some of the feature. It cannot do all that would be required in the final language feature (depending on the final feature definition) but provide some equivalent solutions for template type checking (and other compile time checks).

As suggested, as the C++0x concept is a language features, it would allow to provide more elegant semantic and allow compiler to use informations not currently available to the program, allowing more detailed or intelligent errors at compile time (as the concept first purpose is to allow abstract type check in templates).

Klaim
+2  A: 

Disclaimer: I wasn't able to successfully use BCCL within the last 30 minutes even though I had Boost installed already. The example you see below looks OK according to the BCCL documentation of Boost 1.37 but didn't work. I guess this counts as disadvantage.

With BCCL you only get something like static assertions whereas the core language concept feature provides full modular type checking and is able to prevent some function template from participating in overload resolution. With native concepts the body of a constrained template can be checked immediately by the compiler whereas BCCL doesn't make the compiler check anything in that respect. You have to manually instantiate your template with "arche type" parameters to see if the template uses any operations that are not available (for example, operator-- on forward iterators).

As for overload resolution here's an example:

template<typename Iter>
void foo(Iter,Iter) {
   BOOST_CONCEPT_ASSERT((RandomAccessIterator<Iter>));
}

void foo(long, int);

int main() {
   foo(2,3); // compile-time error
}

The template is a better match because the non-template requires a conversion from int to long. But instantiation fails because int is not an iterator. You get a nice error message explaining it but that's not very satisfying, is it? With native concepts you could have written

template<typename Iter>
  requires RandomAccessIterator<Iter>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

Here, the function template will not take part in overload resolution because the requirement for T=int is not satisfied. Great!

We still get to constrain function templates with the SFINAE trick. C++0x extends SFINAE to expressions and together with decltype and default template arguments for function templates we can write

template<typename T> T&& make();

template<typename Iter, class = decltype( *make<Iter>() )>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

In this case, template argument deduction will fail silently because the compiler doesn't know what the type of the expression ∗make<Iter>() is supposed to be (we can't dereference an int). With a bit of template metaprogramming and some macros we can get pretty close to imposing arbitrary structural constraints on template parameters in a readable way. Here's what it might look like assuming appropriate definitions for REQUIRES and RandomAccessIterator:

template <typename Iter
  REQUIRES( RandomAccessIterator<Iter> )
>
void foo(Iter,Iter) {}

HTH, S

sellibitze
+1 for the REQUIRES emulation idea :)
Johannes Schaub - litb
Thanks! But I can't take full credit for that. I think I saw the use of a default template argument for SFINAE in the clc++m newsgroup.
sellibitze