templates and friends

This is a discussion on templates and friends within the C++ Programming forums, part of the General Programming Boards category; sample error: "Array2.h": Array2.h friend declaration `std: stream& at line 13 operator<<(std: stream&, const Array<T, numberOfElements>&)' declares a non-template function ...

  1. #1
    Registered User
    Join Date
    Dec 2003
    Posts
    167

    templates and friends

    sample error:

    "Array2.h": Array2.h friend declaration `std:stream& at line 13
    operator<<(std:stream&, const Array<T, numberOfElements>&)' declares a non-template function

    What could the problem be?

    Code:
     
    #ifdef __BORLANDC__
      #pragma argsused
    #endif
    #include <iostream>
    
    using std::cout;
    using std::endl;
    
    #include "Array2.h"
    
    int main( int argc, char * argv[])
    {
      Array<int, 6 > numbers;
    
      cout << numbers.getSize() << endl;
      for( int i = 0; i < 6; i++ ){
        numbers[i] = i;
      }
    
      cout << numbers << endl;
    
      return 0;
    }
    Code:
     
    // Fig. 8.4: array1.h
    // Simple class Array (for integers)
    #ifndef ARRAY1_H
    #define ARRAY1_H
    
    #include <iostream>
    
    using std::ostream;
    using std::istream;
    
    template <class T, int numberOfElements = 10>
    class Array {
       friend ostream &operator<<( ostream &, const Array<T,numberOfElements> & );
       friend istream &operator>>( istream &, Array<T,numberOfElements> & );
    public:
       Array<T,numberOfElements>();                   // default constructor
       Array( const Array<T,numberOfElements> & );              // copy constructor
       ~Array<T,numberOfElements>();                            // destructor
       int getSize() const;                 // return size
       const Array<T,numberOfElements> &operator=( const Array<T,numberOfElements> & ); // assign arrays
       bool operator==( const Array<T,numberOfElements> & ) const;  // compare equal
    
       // Determine if two arrays are not equal and
       // return true, otherwise return false (uses operator==).
       bool operator!=( const Array<T,numberOfElements> &right ) const
          { return ! ( *this == right ); }
    
       T &operator[]( int );              // subscript operator
       const T &operator[]( int ) const;  // subscript operator
       static int getArrayCount();          // Return count of
                                            // arrays instantiated.
    private:
       int size; // size of the array
       T *ptr; // pointer to first element of array
       static int arrayCount;  // # of Arrays instantiated
    };
    
    // Fig 8.4: array1.cpp
    // Member function definitions for class Array
    #include <iostream>
    
    using std::cout;
    using std::cin;
    using std::endl;
    
    #include <iomanip>
    
    using std::setw;
    
    #include <cstdlib>
    #include <cassert>
    //#include "array1.h"
    
    // Initialize static data member at file scope
    template <class T, int numberOfElements>
    int Array< T,numberOfElements>::arrayCount = 0;   // no objects yet
    
    // Default constructor for class Array (default size 10)
    template <class T, int numberOfElements >
    Array<T,numberOfElements>::Array()
    {
       size = ( numberOfElements > 0 ? numberOfElements : 10 );
       ptr = new T[ size ]; // create space for array
       assert( ptr != 0 );    // terminate if memory not allocated
       ++arrayCount;          // count one more object
    
       for ( int i = 0; i < size; i++ )
          ptr[ i ] = 0;          // initialize array
    }
    
    // Copy constructor for class Array
    // must receive a reference to prevent infinite recursion
    template <class T, int numberOfElements >
    Array<T,numberOfElements>::Array( const Array<T,numberOfElements> &init ) : size( init.size )
    {
       ptr = new T[ size ]; // create space for array
       assert( ptr != 0 );    // terminate if memory not allocated
       ++arrayCount;          // count one more object
    
       for ( int i = 0; i < size; i++ )
          ptr[ i ] = init.ptr[ i ];  // copy init into object
    }
    
    // Destructor for class Array
    template <class T, int numberOfElements>
    Array<T,numberOfElements>::~Array()
    {
       delete [] ptr;            // reclaim space for array
       --arrayCount;             // one fewer objects
    }
    
    // Get the size of the array
    template <class T, int numberOfElements>
    int Array<T,numberOfElements>::getSize() const { return size; }
    
    // Overloaded assignment operator
    // const return avoids: ( a1 = a2 ) = a3
    template <class T, int numberOfElements>
    const Array<T,numberOfElements> &Array<T,numberOfElements>::operator=( const Array<T,numberOfElements> &right )
    {
       if ( &right != this ) {  // check for self-assignment
    
          // for arrays of different sizes, deallocate original
          // left side array, then allocate new left side array.
          if ( size != right.size ) {
             delete [] ptr;         // reclaim space
             size = right.size;     // resize this object
             ptr = new T[ size ]; // create space for array copy
             assert( ptr != 0 );    // terminate if not allocated
          }
    
          for ( int i = 0; i < size; i++ )
             ptr[ i ] = right.ptr[ i ];  // copy array into object
       }
    
       return *this;   // enables x = y = z;
    }
    
    // Determine if two arrays are equal and
    // return true, otherwise return false.
    template <class T, int numberOfElements>
    bool Array<T,numberOfElements>::operator==( const Array<T,numberOfElements> &right ) const
    {
       if ( size != right.size )
          return false;    // arrays of different sizes
    
       for ( int i = 0; i < size; i++ )
          if ( ptr[ i ] != right.ptr[ i ] )
             return false; // arrays are not equal
    
       return true;        // arrays are equal
    }
    
    // Overloaded subscript operator for non-const Arrays
    // reference return creates an lvalue
    template <class T, int numberOfElements>
    T &Array<T,numberOfElements>::operator[]( int subscript )
    {
       // check for subscript out of range error
       assert( 0 <= subscript && subscript < size );
    
       return ptr[ subscript ]; // reference return
    }
    
    // Overloaded subscript operator for const Arrays
    // const reference return creates an rvalue
    template <class T, int numberOfElements>
    const T &Array<T,numberOfElements>::operator[]( int subscript ) const
    {
       // check for subscript out of range error
       assert( 0 <= subscript && subscript < size );
    
       return ptr[ subscript ]; // const reference return
    }
    
    // Return the number of Array objects instantiated
    // static functions cannot be const
    template <class T, int numberOfElements>
    int Array<T,numberOfElements>::getArrayCount() { return arrayCount; }
    
    // Overloaded input operator for class Array;
    // inputs values for entire array.
    template <class T, int numberOfElements>
    istream &operator>>( istream &input, Array<T,numberOfElements> &a )
    {
       for ( int i = 0; i < a.size; i++ )
          input >> a.ptr[ i ];
    
       return input;   // enables cin >> x >> y;
    }
    
    // Overloaded output operator for class Array
    template <class T, int numberOfElements>
    ostream &operator<<( ostream &output, const Array<T,numberOfElements> &a )
    {
       int i;
    
       for ( i = 0; i < a.size; i++ ) {
          output << setw( 12 ) << a.ptr[ i ];
    
          if ( ( i + 1 ) % 4 == 0 ) // 4 numbers per row of output
             output << endl;
       }
    
       if ( i % 4 != 0 )
          output << endl;
    
       return output;   // enables cout << x << y;
    }
    #endif
    silk.odyssey

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,691
    I don't see anything wrong with the code according to the standard and Comeau had no problems with it either...
    Code:
    Thank you for testing your code with Comeau C/C++!
    Tell others about http://www.comeaucomputing.com/tryitout ! 
    
    Your Comeau C/C++ test results are as follows: 
    
    Comeau C/C++ 4.3.3 (Aug  6 2003 15:13:37) for ONLINE_EVALUATION_BETA1
    Copyright 1988-2003 Comeau Computing.  All rights reserved.
    MODE:strict errors C++
    
    In strict mode, with -tused, Compile succeeded 
    (but remember, the Comeau online compiler does not link).
    >> What could the problem be?
    Your compiler.

    GCC 3.2 (mingw special 20020817-1) also issued a similiar warning message, which can be suppressed using "-Wno-non-template-friend". There is a historical reason for having the warning enabled by default - you can read about it here.

    Even after adding "-Wno-non-template-friend", GCC doesn't instantiate the friend template functions correctly. This can be fixed by giving GCC a "hint" to the fact that the friend declarations are indeed template functions....
    Code:
    friend ostream &operator << <>( ostream &, const Array<T,numberOfElements> & );
    friend istream &operator >> <>( istream &, Array<T,numberOfElements> & );
    MSVC also has problems with friend template functions which can be worked around by implementing the friend function right inside the template class declaration - doing this will also make GCC happy in this case.
    I typically implement all my friend template functions right there inside the class in order to be compatible with as many compilers as possible.

    gg
    Last edited by Codeplug; 06-11-2004 at 05:19 PM.

  3. #3
    Registered User
    Join Date
    Dec 2003
    Posts
    167
    Codeplug,

    Thanks for the help, the hint makes gcc happy but vc7 still complains. When I implement the friend functions in the class it compiles with vc7 but i get a linker error about no entry point. Borland c++ says that functions containing for are not expanded inline and "main" cannot be a template function.
    silk.odyssey

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,691
    You've got a syntax error somewhere that's messing up the parsing of main()....

    Did you do it like this?
    Code:
    template <class T, int numberOfElements = 10>
    class Array {
        friend ostream &operator<<( ostream &output, const Array<T,numberOfElements> &a )
        {
            //...code
        }
    
        friend istream &operator>>( istream &input, Array<T,numberOfElements> &a )
        {
            //...code
        }
    public:
    //...rest of Array
    };
    
    // remove original implementation
    gg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Int to String Functions?
    By drdroid in forum C++ Programming
    Replies: 8
    Last Post: 02-21-2003, 11:20 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21