Prev: outer class `this` in local classes without inheritance?
Next: why are missing return statements from non-void functions not a compilation error
From: Elias Salomão Helou Neto on 26 Jul 2010 23:44 template< unsigned dimen, class vtype = const unsigned& > struct n_vect { vtype head; n_vect< dimen - 1, vtype > rest; }; template< class vtype > struct n_vect< 1u, vtype > { vtype head; }; void myFun( n_vect< 10 >& par ) { } int main() { n_vect< 10 > test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // OK (0) myFun( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ); // Error (at least for gcc). Why? (1) myFun( n_vect< 10 > = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ); // Another error, now understandable (2) myFun( n_vect< 10 >{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ); //compiles if -std=c++0x is used (3) return( 0 ); } I will later explain the practical motivation for it (this will make clear why the code is templated), but for now let us focus on the fact that, at least from gcc's point of view, the new standard allows (3) but not (1). If disallowing (1) is not a bug in gcc, I think that adding it to the language would be a major improvement. Much more than the flawed std::initializer_list approach, why it is flawed will become clear in the case study below. Notice that the semantics is obvious: create an unnamed temporary struct as if (0) had been used. Furthermore it interacts nicely with the rest of the language and will introduce no bugs: it will be allowed only when creating a const reference in a function call. It will not break existing code since current working code cannot use this construct. Notice that myFun is not a template but that could be possible either under some fairly reasonable rules (namely, if the function is templated by an integral type, say n, and the concrete type generated by instatiating the template with n = ( length of the {}-list ) is initializable with the {}-list, consider it a match). Overload resolution would follow the same rules as usual, just look for signatures with POD types of the proper size, if they do not exist choke. Stop also if there is an ambiguity in any case. ++++++++++++++++++++++++++++++++++++++++++ Now for the case that motivated the example. While it may be true that there are reasons for matrices to be kept outside of STL, it is a pity that no truly natural, efficient and general (in n) implementation of an n-dimensional (with n being an arbitrary compile-time constant) exists. By efficient I mean that should behave, up to some optimizations (such as reference shortcuting even within nested structs) exactly as handcrafted code for that specific dimension. And by matural, I mean that members with template-dependent number of parameter should be as similar to I think C++ power comes much more from templates than from OO, but obviosuly both mechanisms together interact very well and classes provide means of organizing and simplifying code that are invaluable. However, when it comes to true metaprogramming, C++ only make us want more. The simple extension I propose could empower the language by allowing a natural way to build, at compile-time, very efficient variable argument functions. It would also render std::initialization_list useless! Notice that with that we could have a template template < unsigned dim > class nd_matrix{ public: nd_matrix( const n_vect < dim>& size ); size_type& operator()( const n_vect< dim >& pos ); template< unsigned n > nd_matrix< n > reshape( const n_vect< n >& new_size ); ... }; And use things like nd_matrix< 5 > A( { 1, 2, 3, 4, 5 } ); A( { 0, 0, 2, 3, 4 } ) = 1; nd_matrix< 1 > B( { 120 } ); B = A.reshape( { 120 } ); // Will match A.reshape< 1 > nd_matrix< 2 > C( B.reshape( { 3, 2, 20 } ) ); We could also endow the STL containers, say std::vector, with template< class T > template< unsigned n > vector< T >::vector< T >( const n_vect& data ); and achieve the nicest effect: std::vector< double > TeXVersions( { 3, 3.1, 3.14, 3.141 } ); or, if you would like: std::vector< double > TeXVersions = { 3, 3.1, 3.14, 3.141 } ; one could apply automatic conversion rules for overloading resolution: whenever there is no signature with an "exact" POD data match, try using a conversion. Now, std::initializer_list is a glorified simple container, that happens to carry an extra memory for its size (take a look at http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.4/a01223.html for an example implementation) and does not allow for the optimizations we require. When using n-dimensional array access within a tight loop, it is unacceptable to have the extra burden of using a run-time loop instead of a unrolled version to calculate the position of the data, forcing the user to drop std::initializer_list in favor of manually coding. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + To summarize, the n_vect technique above would: 1) Allow loop unrolling and other efficient automatic code generation methods; 2) Perfectly replace std::initializer_list in every of its uses by means of the technique exemplified by reshape; So, why not? Should I submit a defect report? Or am I completely wrong? Thank you for your time, Elias. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |