I l@ve RuBoard Previous Section Next Section

Solution

graphics/bulb_icon.gif

Let's analyze the solution above and see how well it measures up to what the question asked. Recall that the original question was: How can you best implement copy construction and copy assignment for the following fixed-length vector class? How can you provide maximum usability for construction and assignment? Hint: Think about the kinds of things that client code might want to do.

Copy Construction and Copy Assignment

First, note that the first question is a red herring. Did you spot it? The original code already had a copy constructor and a copy assignment operator that worked just fine, thank you very much. Our solution proposes to address the second question by adding a templated constructor and a templated assignment operator to make construction and assignment more flexible.



template<typename O, size_t osize> 


fixed_vector( const fixed_vector<O,osize>& other )


{


  copy( other.begin(),


        other.begin()+min(size,osize),


        begin() );


}





template<typename O, size_t osize>


fixed_vector<T,size>&


operator=( const fixed_vector<O,osize>& other )


{


  copy( other.begin(),


        other.begin()+min(size,osize),


        begin() );


  return *this;


}


Note that the above functions are not a copy constructor and a copy assignment operator. Here's why: A copy constructor or copy assignment operator specifically constructs/assigns from another object of exactly the same type梚ncluding the same template arguments, if the class is templated. For example:



struct X 


{


  template<typename T>


  X( const T& );    // NOT copy constructor, T can't be X





  template<typename T>


  operator=( const T& );


                    // NOT copy assignment, T can't be X


};


"But," you say, "those two templated member functions could exactly match the signatures of copy construction and copy assignment!" Well, actually, no梩hey couldn't, because in both cases, T may not be X. To quote from the standard (12.8/2, note 4):

Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

There's similar wording for the copy assignment operator (in 12.8/9 note 7). So the proposed solution, in fact, still has the same copy constructor and copy assignment operator as the original code, because the compiler still generates the implicit versions. What we've done is extend the construction and assignment flexibility, not replace the old versions.

For another example, consider the following program:



fixed_vector<char,4> v; 


fixed_vector<int,4>  w;


fixed_vector<int,4>  w2(w);


        // calls implicit copy constructor


fixed_vector<int,4>  w3(v);


        // calls templated conversion constructor


w = w2; // calls implicit copy assignment operator


w = v;  // calls templated assignment operator


So the question really wanted us to provide flexible "construction and assignment from other fixed_vectors," not specifically flexible "copy construction and copy assignment," which already existed.

Usability Issues for Construction and Assignment

There are two major usability considerations.

  1. Support varying types (including inheritance).

    While fixed_vector definitely is and should remain a homogeneous container, sometimes it makes sense to construct or assign from another fixed_vector that actually contains different objects. As long as the source objects are assignable to our type of object, this should be allowed. For example, clients may want to write something like this:

    
    
    fixed_vector<char,4> v; 
    
    
    fixed_vector<int,4>  w(v);  // templated construction
    
    
    w = v;                      // templated assignment
    
    
    
    
    
    class B            { /*...*/ };
    
    
    class D : public B { /*...*/ };
    
    
    
    
    
    fixed_vector<D*,4> x;
    
    
    fixed_vector<B*,4> y(x);    // templated construction
    
    
    y = x;                      // templated assignment
    
    
    

    This is legal and works as expected because a D* can be assigned to a B*.

  2. Support varying sizes.

    Similarly, clients may want to construct or assign from fixed_vectors with different sizes. Again, it makes sense to support this feature. For example:

    
    
    fixed_vector<char,6> v; 
    
    
    fixed_vector<int,4>  w(v);  // initializes using 4 values
    
    
    w = v;                      // assigns using 4 values
    
    
    class B            { /*...*/ };
    
    
    class D : public B { /*...*/ };
    
    
    fixed_vector<D*,16> x;
    
    
    fixed_vector<B*,42> y(x);   // initializes using 16 values
    
    
    y = x;                      // assigns using 16 values
    
    
    
    I l@ve RuBoard Previous Section Next Section