c++c++11initializer-list

C++0x, Compiler hooks and hard coded languages features


I'm a little curious about some of the new features of C++0x. In particular range-based for loops and initializer lists. Both features require a user-defined class in order to function correctly.

I came accross this post, and while the top-answer was helpful. I don't know if it's entirely correct (I'm probably just completely misunderstanding, see 3rd comment on first answer). According to the current specifications for initializer lists, the header defines one type:

template<class E> class initializer_list {
public:
    initializer_list();

    size_t size() const; // number of elements
    const E* begin() const; // first element
    const E* end() const; // one past the last element
};

You can see this in the specifications, just Ctrl + F 'class initializer_list'.

In order for = {1,2,3} to be implicitly casted into the initializer_list class, the compiler HAS to have some knowledge of the relationship between {} and initializer_list. There is no constructor that receives anything, so the initializer_list as far as I can tell is a wrapper that gets bound to whatever the compiler is actually generating.

It's the same with the for( : ) loop, which also requires a user-defined type to work (though according to the specs, updated to not require any code for arrays and initializer lists. But initializer lists require <initializer_list>, so it's a user-defined code requirement by proxy).

Am I misunderstanding completely how this works here? I'm not wrong in thinking that these new features do infact rely extremely heavily on user code. It feels as if the features are half-baked, and instead of building the entire feature into the compiler, it's being half-done by the compiler and half done in includes. What's the reason for this?

Edit: I typed 'rely heavily on compiler code', and not 'rely heavily on user code'. Which I think completely threw off my question. My confusion isn't about new features being built into the compiler, it's things that are built into the compiler that rely on user code.


Solution

  • I'm not wrong in thinking that these new features do infact rely extremely heavily on compiler code

    They do rely extremely on the compiler. Whether you need to include a header or not, the fact is that in both cases, the syntax would be a parsing error with today compilers. The for (:) does not quite fit into todays standard, where the only allowed construct is for(;;)

    It feels as if the features are half-baked, and instead of building the entire feature into the compiler, it's being half-done by the compiler and half done in includes. What's the reason for this?

    The support must be implemented in the compiler, but you are required to include a system's header for it to work. This can serve a couple of purposes, in the case of initialization lists, it brings the type (interface to the compiler support) into scope for the user so that you can have a way of using it (think how va_args are in C). In the case of the range-based for (which is just syntactic sugar) you need to bring Range into scope so that the compiler can perform it's magic. Note that the standard defines for ( for-range-declaration : expression ) statement as equivalent to ([6.5.4]/1 in the draft):

    { 
       auto && __range = ( expression ); 
       for ( auto __begin = std::Range<_RangeT>::begin(__range), 
             __end = std::Range<_RangeT>::end(__range); 
             __begin != __end; 
             ++__begin ) { 
          for-range-declaration = *__begin; 
          statement 
       } 
    } 
    

    If you want to use it only on arrays and STL containers that could be implemented without the Range concept (not in the C++0x sense), but if you want to extend the syntax into user defined classes (your own containers) the compiler can easily depend upon the existing Range template (with your own possible specialization). The mechanism of depending upon a template being defined is equivalent to requiring a static interface on the container.

    Most other languages have gone in the direction of requiring a regular interface (say Container,...) and using runtime polymorphism on that. If that was to be done in C++, the whole STL would have to go through a major refactoring as STL containers do not share a common base or interface, and they are not prepared to be used polimorphically.

    If any, the current standard will not be underbaked by the time it goes out.