Thread: Unresolved externals - please help before I throw my laptop away

  1. #1
    Registered User
    Join Date
    Mar 2009
    Posts
    43

    Unresolved externals - please help before I throw my laptop away

    Hi all...

    Second thread start in 24 hours... :-S

    I'm having a nightmare trying to get the below code to link, I keep getting unresolved externals errors from the linker. It seems to be related to use of the overloaded operator << in the "main" function, which is being used to print each object in an array created from a template.

    Apologies for the looooong code... thought best to just print the lot rather than being asked for it later!...

    Code:
    #include <iostream>
    using namespace std;
    
    const int defaultSize = 3;
    
    class Dog
    {
    public:
    	Dog ();
    	Dog (int);
    	~Dog ();
    
    	int getAge () const {return itsAge;}
    	void setAge (int age) {itsAge = age;}
    
    	friend ostream& operator << (ostream&, const Dog&); 
    
    private:
    	int itsAge;
    };
    
    Dog::Dog () : itsAge(0)
    {
    	cout << "Dog default constructor...";
    }
    
    Dog::Dog (int age) : itsAge (age)
    {
    	cout << "Dog constructor with parameter...";
    }
    Dog::~Dog()
    {
    	cout << "Destructed a dog :-(...";
    }
    ostream& operator << (ostream& output, const Dog& rhs) 
    {
    	output << rhs.getAge(); // equates to cout << itsAge;
    	return output;
    }
    
    template <class T> 
    class Array
    {
    public:
    	Array (int itsSize = defaultSize); 
    	Array (const Array &rhs); 
    	~Array () {delete [] pType;} 
    	Array& operator= (const Array &);
    	T& operator [] (int offset) {return pType[offset];}
    	const T& operator [] (int offset) const {return pType[offset];} 
    	int getSize () const {return itsSize;}
    	friend ostream& operator << (ostream&, const Array<T>&);
    private:
    	T * pType;
    	int itsSize;
    };
    
    template <class T> Array<T>::Array(int size = defaultSize) : itsSize(size)
    {
    	pType = new T [size]; 
    	for (int i = 0; i < size; i++)
    		pType[i] = (T)0; 
    }
    template <class T> Array<T>::Array(const Array &rhs)
    {
    	itsSize = rhs.getSize();
    	pType = new T[itsSize];
    	for (int i = 0; i < itsSize; i++)
    		pType[i] = rhs[i]; // uses const operator []
    }
    
    template <class T> Array<T>& Array<T>::operator = (const Array &rhs)
    {
    	if (this == &rhs)
    		return *this;
    	delete [] pType;
    	pType = new T[itsSize];
    	for (int i = 0; i < itsSize; i++)
    		pType[i] = rhs[i]
    }
    Array<Dog>::Array(int AnimalArraySize) : 
    itsSize(AnimalArraySize)
    {
    	pType = new Dog[AnimalArraySize];
    }
    
    template <class T>
    ostream& operator << (ostream& output, const Array<T>& theArray) 
    {
    	for (int i=0; i < theArray.getSize(); i++)
    		output << "[" << i << "]: " << theArray[i] << endl;	
                    return output;
    }
    
    void intFillFunction (Array<int>& intArray);
    void dogFillFunction (Array<Dog>& theKennel);
    
    int main ()
    {
    	Array<int> intArray; 
    	Array<Dog> theKennel; 
    	intFillFunction (intArray);
    	dogFillFunction (theKennel);
    	cout << intArray << endl; // CAUSING UNRESOLVED EXTERNAL PROBLEM
    	cout << theKennel << endl; // CAUSING UNRESOLVED EXTERNAL PROBLEM
    	return 0;
    }
    void intFillFunction (Array<int>& theArray) 
    {
    	bool Stop = false;
    	int offset, value;
    	while (!Stop)
    	{
    		cout << "Enter an offset (0-2) and a value. (-1 to stop)";
    		cin >> offset >> value;
    		if (offset < 0) 
    			break;
    		if (offset > 2)
    		{
    			cout << "Value between 0-2 only please.\n";
    			continue; 
    		}
    		theArray[offset] = value; 
    	}
    }
    void dogFillFunction (Array<Dog>& dogArray)
    {
    	Dog * pDog;
    	for (int i = 0; i < dogArray.getSize(); i++)
    	{
    		pDog = new Dog (i*3);
    		dogArray[i] = *pDog;
    		delete pDog; 
    		pDog = 0;
    	}
    }
    I've been over the code with a fine tooth comb - I can't find any typos or something that would cause functions declarations not to match with their definitions (although I've just copy/pasted this from Visual Express so don't be surprised if I've accidentally deleted a semi colon or something). Please help me before I throw my rather nice laptop out the lounge window....

    Also can anyone give me any tips for tracking down linker errors? Visual Express can't track them down in the same way it can with complier errors... cheers :-)

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    It is not that easy to track down linker errors, as you have discovered. That's mainly because the compiler doesn't actually tell the linker where the source code came from that asks for that particular function. Not much we can do about that, unfortunately.

    Trying to understand what the function is that is missing will definitely help.

    In this case, the problem is clearly that for whatever reason, the compiler doesn't want to instantiate
    Code:
    template <class T>
    ostream& operator << (ostream& output, const Array<T>& theArray) 
    {
    	for (int i=0; i < theArray.getSize(); i++)
    		output << "[" << i << "]: " << theArray[i] << endl;	
                    return output;
    }
    for your arrays. I have no idea why.

    Your indentation is a bit weird...

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Mar 2009
    Posts
    43
    Anyone have any idea why? Is it just because the express version of visual is free for a reason? Or (far more likely) have I messed it up somewhere?
    Last edited by Know_Your_Role; 06-03-2009 at 08:32 AM.

  4. #4
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Code:
    pType[i] = rhs[i] // missing ';'
    In defining the constructor:

    Code:
    template <class T> Array<T>::Array
    Should be:

    Code:
    template <class T> Array<T>::Array<T>
    When you declare a default argument in a member function, you must omit it in the definition:

    Code:
    template <class T> Array<T>::Array<T>(int size)
    operator << is really a template function external of the class, so you need to declare it as such:

    Code:
    template <class T> 
    class Array
    {
    // ...
        template <typename U>
        friend ostream& operator << (ostream&, const Array<U>&);
    // ...
    };
    Optionally, you can define the body in the class, in which case the template is implied:

    Code:
    template <class T> 
    class Array
    {
    // ...
        friend ostream& operator << (ostream& output, const Array& theArray)
        {    
            for (int i=0; i < theArray.getSize(); i++)
                output << "[" << i << "]: " << theArray[i] << endl;    
                return output;    
        }
    // ...
    };
    To specialize the Array<Dog> constructor, you need to use this syntax:

    Code:
    template<>
    Array<Dog>::Array<Dog>(int AnimalArraySize) : 
    itsSize(AnimalArraySize)
    {
        pType = new Dog[AnimalArraySize];
    }
    Finally, although GCC didn't complain about this:

    Code:
    template <class T> Array<T>::Array<T>(const Array &rhs)
    For consistancy, it should be:

    Code:
    template <class T> Array<T>::Array<T>(const Array<T> &rhs)
    Last edited by Sebastiani; 06-03-2009 at 08:44 AM.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  5. #5
    Registered User
    Join Date
    Mar 2009
    Posts
    43
    Sorry you're speaking to a bit of a noob here (looks embarassed!!) are you saying there's something wrong with my array constructor? And is this why I'm having my unresolved externals problem? If so why?

    lol thanks for your patience!!

  6. #6
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    I think I fixed the formatting problems. See if it makes sense now...

    EDIT: Here's a 'fixed' version to compare with:

    Code:
    #include <std>
    const int defaultSize = 3;
    
    class Dog
    {
    public:
    	Dog ();
    	Dog (int);
    	~Dog ();
    
    	int getAge () const {return itsAge;}
    	void setAge (int age) {itsAge = age;}
    
    	friend ostream& operator << (ostream&, const Dog&); 
    
    private:
    	int itsAge;
    };
    
    Dog::Dog () : itsAge(0)
    {
    	cout << "Dog default constructor...";
    }
    
    Dog::Dog (int age) : itsAge (age)
    {
    	cout << "Dog constructor with parameter...";
    }
    Dog::~Dog()
    {
    	cout << "Destructed a dog :-(...";
    }
    ostream& operator << (ostream& output, const Dog& rhs) 
    {
    	output << rhs.getAge(); // equates to cout << itsAge;
    	return output;
    }
    
    template <class T> 
    class Array
    {
    public:
    	Array (int itsSize = defaultSize); 
    	Array (const Array &rhs); 
    	~Array () {delete [] pType;} 
    	Array& operator= (const Array &);
    	T& operator [] (int offset) {return pType[offset];}
    	const T& operator [] (int offset) const {return pType[offset];} 
    	int getSize () const {return itsSize;}
    	template <typename U>
    	friend ostream& operator << (ostream& output, const Array<U>& theArray);
    private:
    	T * pType;
    	int itsSize;
    };
    
    template <class T> Array<T>::Array<T>(int size) : itsSize(size)
    {
    	pType = new T [size]; 
    	for (int i = 0; i < size; i++)
    		pType[i] = (T)0; 
    }
    template <class T> Array<T>::Array<T>(const Array<T> &rhs)
    {
    	itsSize = rhs.getSize();
    	pType = new T[itsSize];
    	for (int i = 0; i < itsSize; i++)
    		pType[i] = rhs[i]; // uses const operator []
    }
    
    template <class T> Array<T>& Array<T>::operator = (const Array<T> &rhs)
    {
    	if (this == &rhs)
    		return *this;
    	delete [] pType;
    	pType = new T[itsSize];
    	for (int i = 0; i < itsSize; i++)
    		pType[i] = rhs[i];
    }
    
    template<>
    Array<Dog>::Array<Dog>(int AnimalArraySize) : 
    itsSize(AnimalArraySize)
    {
    	pType = new Dog[AnimalArraySize];
    }
    
    template <class T>
    ostream& operator << (ostream& output, const Array<T>& theArray) 
    {
    	for (int i=0; i < theArray.getSize(); i++)
    		output << "[" << i << "]: " << theArray[i] << endl;	
                    return output;
    }
    
    void intFillFunction (Array<int>& intArray);
    void dogFillFunction (Array<Dog>& theKennel);
    
    int main ()
    {
    	Array<int> intArray; 
    	Array<Dog> theKennel; 
    	intFillFunction (intArray);
    	dogFillFunction (theKennel);
    	cout << intArray << endl; 
    	cout << theKennel << endl;
    	return 0;
    }
    void intFillFunction (Array<int>& theArray) 
    {
    	bool Stop = false;
    	int offset, value;
    	while (!Stop)
    	{
    		cout << "Enter an offset (0-2) and a value. (-1 to stop)";
    		cin >> offset >> value;
    		if (offset < 0) 
    			break;
    		if (offset > 2)
    		{
    			cout << "Value between 0-2 only please.\n";
    			continue; 
    		}
    		theArray[offset] = value; 
    	}
    }
    void dogFillFunction (Array<Dog>& dogArray)
    {
    	Dog * pDog;
    	for (int i = 0; i < dogArray.getSize(); i++)
    	{
    		pDog = new Dog (i*3);
    		dogArray[i] = *pDog;
    		delete pDog; 
    		pDog = 0;
    	}
    }
    Last edited by Sebastiani; 06-03-2009 at 08:47 AM.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  7. #7
    Registered User
    Join Date
    Mar 2009
    Posts
    43
    OK. I'll take a look when I can (I'm at work now lol) and will get back with either more noob-ish questions or a big thank you!

    J :-)

  8. #8
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    A couple of other issues:

    Code:
    pType[i] = (T)0;
    This doesn't make sense for all types, I would recommend using instead:

    Code:
    pType[i] = T();
    And in Array<T>:perator = (const Array<T> &rhs), you're forgetting to set 'itsSize'.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  9. #9
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The linker error appeared to result from these warnings concerning the friend operator<< declaration:

    52: warning: friend declaration `std::ostream& operator<<(std::ostream&, const Array<T>&)' declares a non-template function
    52: warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning
    This is explained here.

    In your case the solution might be: don't declare it a friend, seeing that is implemented entirely in terms of Array's public interface.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  10. #10
    Registered User
    Join Date
    Mar 2009
    Posts
    43
    RE: Sebastiani

    Had a chance to take a look at this during my afternoon break and your explanation / correction regarding operator << makes sense and solved the problem :-D so thank you soooooooooo much!!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Compiling sample DarkGDK Program
    By Phyxashun in forum Game Programming
    Replies: 6
    Last Post: 01-27-2009, 03:07 AM
  2. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  3. C++ std routines
    By siavoshkc in forum C++ Programming
    Replies: 33
    Last Post: 07-28-2006, 12:13 AM
  4. Including lib in a lib
    By bibiteinfo in forum C++ Programming
    Replies: 0
    Last Post: 02-07-2006, 02:28 PM
  5. debug to release modes
    By DavidP in forum Game Programming
    Replies: 5
    Last Post: 03-20-2003, 03:01 PM