Originally Posted by
Mario F.
Concepts
The notion of concepts seems to me pretty simple. It's the type of a type.
Not strictly correct. A concept is a set of assumptions that are made about a type. These assumptions are formalized as a set of nested types in the type, operations you can perform on the type, and similar. However, the documentation of a concept will usually carry additional semantic requirements on these operations.
If you're familiar with Java-like, .Net-like or COM-like interfaces, a concept could be called a compile-time interface.
That type, "Container", is the so called Concept. It seems it will be nothing more than an abstract class of which the container types will derive.
It's not a base class or abstract class or anything like that. Concepts are purely compile-time constructs, enforced for template parameters at compile time. Several different template parameters will still spawn several instantiations, even if they conform to the same concept. The for_each implementations might look like this:
Code:
template<typename InIt, typename Fn> where <InputIterator<InIt>, UnaryFunction<Fn>>
Fn for_each(InIt first, InIt last, Fn fn)
{
for( ; first != last; ++first) fn(*first);
return fn;
}
template<typename Rng, typename Fn> where <InputRange<Rng>, UnaryFunction<Fn>>
Fn for_each(Rng rng, Fn fn)
{
return for_each(rng.begin(), rng.end(), fn);
}
Pass in a container, and the compiler will notice that it conforms to the InputRange concept (it has a begin() and an end(), both of which return InputIterators), so it will choose the second overload. It will then instantiate a version that is specific to that particular container. No runtime switches.
Other concepts will probably be Iterator and possibly(?) more specialized versions of the Container concept, like SequencialContainer(?) and AssociativeContainer(?).
And many refinements of Iterator, and Range and its refinements, and of course all the concepts you can define yourself. Not to mention DefaultConstructible, CopyConstructible, Assignable, and many other things that make useful restrictions on arguments to the STL.
Will this not make templating around STL objects harder to achieve?
I don't see how.
There cannot be many assumptions about the type inside the templated function or class.
Perhaps, but the purpose of concepts is to document the assumptions that are valid, and enforce them.
STL objects are invariably very different objects among themselves. So, it seems to me almost useless to create a template with Concept parameters.
Not at all! Consider what happens if I pass my own custom class ostream_range to for_each. ostream_range has begin() and end(), which return ostream_iterators. ostream_iterators, as is obvious, conform to the OutputIterator concept. Thus, ostream_range conforms to the OutputRange object.
OK, what happens on a C++98 compiler, which has the above overloads of for_each, but no concept checking - it relies on the argument count for the overload.
Most likely, I'll get an error message from inside the three-argument for_each, which says something like
Code:
ERROR C9783 C:\long\path\to\standard\includes\algorithm (214): Cannot convert
std::ostream_iterator<_Element>::__assign_proxy to int - no appropriate conversion operator found
with
[
_Element = int
]
C:\long\path\to\standard\includes\algorithm (212): in std::for_each(_InIt __first, _InIt __last, _Fn __fn)
with
[
_InIt = std::ostream_iterator<int>,
_Fn = void (*)(int)
]
C:\long\path\to\standard\includes\algorithm (227): instantiated from std::for_each(_Rng __rng, _Fn __fn)
with
[
_Rng = cornedbee::tools::ostream_range<int>,
_Fn = void (*)(int)
]
D:\work\projects\example\faulty.cpp (44): instantiated from here
And I'm like, Huh?
Now let's take the C++0x compiler and see what it gives me:
Code:
ERROR C8320 D:\work\projects\example\faulty.cpp (44): Cannot pass orng to std::for_each(_Rng __rng, _Fn __fn):
Object does not fulfill the InputRange concept.
A lot more informative, wouldn't you say?
In fact, it seems a recipe for disaster except for very specific situations that perhaps wouldn't warrant such a big change to the STL.
Where is the danger?
Also, will this not add a lot of weight to the STL performance? Every STL object being now inside a OO structure with a base abstract class, especially when accessed through a pure virtual function, will be leaps and bounds slower than the current design. Won't it?
It won't. As I said, concepts are a purely compile-time mechanism. There is no trace of them left at runtime.
Type Inference
This one boggles me to no end. the auto keyword is dropped of its previous use
Not that I know of. I don't think it was ever valid to use the auto storage modifier without an actual type. It will still exist as a storage modifier. (As such, "auto auto" should be valid.)
and is now used to define an object of which type is infered from the initializer. So...
Code:
int foo = 12;
auto bar = foo; // bar type is obtained by infering the type of foo. bar is an int.
From what I have read this is nothing but syntactic sugar.
Indeed. Very, very important syntactic sugar, if you've ever played with complicated types like Boost.Spirit's parsers.
What will distinguish const_iterator from iterator? Surely not the compiler! Will it?
Nothing does. Except this: right now, hardly anybody uses const_iterator if the source object isn't const, because it's too much to write. If the object IS const, then the compiler will correctly deduct const_iterator for auto. The non-const version of begin() won't be considered, because the object is const.
Also, there's a proposal to add cbegin() and cend() etc. to all containers that are not overloaded on constness. I'm not sure if that proposal will go through.
And what more... literals. What to say of auto x = 12.5? It seems logic to believe that the implicit conversion rules will dictate the type of x. But according to these rules x would be a unsigned long double. Highly excessive, don't you think?
Uh ... there is no such type as an unsigned long double. The type of a suffix-less decimal constant expression is double, and that will be the deduced type of x, too. There's absolutely no ambiguity there, and no implicit conversion rules come into play. The nice thing about auto is that you're guaranteed there won't be any conversion on initialization.
It will also introduce yet another machine dependant construct.
No, it isn't at all machine-dependent. The types of literals are well-defined by the standard.
But what makes this one worst is that the auto keyword will explicitly hide the type of the object... forever! Only through RTTI will it be possible to effectively debug the code. Worst, there's no RTTI for built-in types. So... What on earth! Am I missing something?
You can just do the same as the compiler does and deduce the type of the object from its initializer.
But more importantly, and this is what boggles me more, will this not break backwards compatibility with C?
No. As I said, the auto keyword will retain its functionality as a storage modifier.
Originally Posted by
ssjnamek
most of that is too advanced for me but the last part you dont want backwards compaitibility with C if so that is probably one of the dumbest things ive ever heard.
You might want to be very careful with such statements. Backwards compatibility always was and still is a stated goal of C++.