Author: | James Widman & Thorsten Ottosen |
---|---|
Contact: | widman -> gimpel dot com & thorsten dot ottosen -> dezide dot com |
Organizations: | Gimpel Software & Dezide Aps |
Date: | 2009-04-28 |
Number: | N2881=09-0071 |
Working Group: | Evolution |
Abstract
[NOTE! We strongly suggest that EWG ignore this proposal until after the FDIS for C++0x ships.]
This paper provides motivation and a design proposal for base class aliases. A base class alias simplifies the naming of a base class by enabling the use of the new alias-declaration syntax (see [dcl.typedef]) as part of the syntax for a base-specifier in order to define a typedef name for the base class right inside the base-specifier.
The motivation is simple: when the name of a base class is a template-id, repeating it becomes unpalatable. For example, after preprocessing, one popular implementation of std::bitset looks like this:
template<size_t _Nb> class bitset : private _Base_bitset < ((_Nb) < 1 ? 0 : ((_Nb) + numeric_limits<unsigned long>::digits - 1) / numeric_limits<unsigned long>::digits) > { private: typedef _Base_bitset < ((_Nb) < 1 ? 0 : ((_Nb) + numeric_limits<unsigned long>::digits - 1) / numeric_limits<unsigned long>::digits) > _Base; // [...] };
We propose the introduction of a small pure language extension that enables the definition of a typedef name for a base class at the point of its base-specifier . The syntax would echo both
Example: With base class aliases, the definition of bitset could be rewritten to the following (somewhat-less appalling) version:
template<size_t _Nb> class bitset : _Base = private _Base_bitset < ((_Nb) < 1 ? 0 : ((_Nb) + numeric_limits<unsigned long>::digits - 1) / numeric_limits<unsigned long>::digits) > // '_Base' is now in scope here { // The name '_Base' is also injected here // as a private typedef name (along // with the injected-class-name). // [...] };
Here is another example from a fairly simple Boost library
template < class T, class CloneAllocator, class Allocator > class ptr_vector : public ptr_sequence_adapter< T, std::vector<void*,Allocator>, CloneAllocator > { typedef ptr_sequence_adapter< T, std::vector<void*,Allocator>, CloneAllocator > base_class;
Again, the length of the template-id naming the base class is an enemy of readability and maintainability. With base class aliases we could rewrite the example as:
template < class T, class CloneAllocator, class Allocator > class ptr_vector : base_class = public ptr_sequence_adapter< T, std::vector<void*,Allocator>, CloneAllocator > {
The benefits are compounded when one base-specifier depends on another. Example:
template< class T, class U, class V > class Foo : base1 = public some_base<T,U,V>, base2 = public another_base<base1,T,U,V>, // ... {
We can summarise the advantages of this feature as follows:
We believe the proposal is straightforward to implement.
We are not yet aware of any novel adverse effects that would be invited by this proposal. Indeed, there should be few or none because it merely appends to the list of syntactic contexts where a typedef name can be introduced into some scope.
The access of the injectecd base alias name is an open issue. Consider the following example
class Foo : base1 = public some_base, base2 = protected another_base, base3 = private yet_another_base {}; typedef Foo::base1 base1; // OK? typedef Foo::base3 base3; // ERROR, 'base3' is a private name class Bar : public Foo { typedef base2 foos_2nd_base; // OK? };
We suspect that the syntax should enable the user to explicitly indicate the access of the injected typedef name:
class Foo : public base1 = public some_base, protected base2 = protected another_base, private base3 = private yet_another_base {}; typedef Foo::base1 base1; // Ok, base1 is a public membr of Foo. typedef Foo::base3 base3; // ERROR, 'base3' is a private name class Bar : public Foo { typedef base2 foos_2nd_base; // Ok, base2 is a protected // member of Foo. };
This leaves us with one open question: when the alias definition does not have an access-specifier, what access do we assign to it? What's the "sensible default"? Here are some possible answers to that question: